<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>yeonjue-2.log</title>
        <link>https://velog.io/</link>
        <description>하루에 한 개념씩</description>
        <lastBuildDate>Fri, 20 Feb 2026 08:02:21 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>yeonjue-2.log</title>
            <url>https://velog.velcdn.com/images/ilov-/profile/862f50ee-48d8-4cc6-bd87-079dd9fb8157/image.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. yeonjue-2.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/ilov-" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[Hexagonal Architecture]]></title>
            <link>https://velog.io/@ilov-/Hexagonal-Architecture</link>
            <guid>https://velog.io/@ilov-/Hexagonal-Architecture</guid>
            <pubDate>Fri, 20 Feb 2026 08:02:21 GMT</pubDate>
            <description><![CDATA[<h1 id="clean-architecture">Clean Architecture</h1>
<p><img src="https://velog.velcdn.com/images/ilov-/post/0be07dd8-968f-47dd-a5d5-32e8258c3511/image.png" alt=""></p>
<ul>
<li>핵심 규칙은 도메인 코드가 바깥으로 향하는 어떤 의존성도 없어야 한다는 것</br>
</br>
</br>

</li>
</ul>
<h1 id="layered-architecture">Layered Architecture</h1>
<p><img src="https://velog.velcdn.com/images/ilov-/post/0d6ebb12-7293-4d11-ae03-c25372744956/image.png" alt=""></p>
<ul>
<li>같은 목적의 코드들을 같은 계층으로 그룹화한 것으로, 역할과 관심사를 계층으로 분리</li>
<li>구조: Presentation(UI) → Business(Service) → Data Access(DB)</li>
<li>상위 계층이 하위 계층에 의존하며, 데이터가 위에서 아래로 흐름</li>
<li>구현이 빠르고 단순하지만, 비즈니스 로직이 특정 DB 기술(JPA 등)에 강하게 결합되기 쉬움</li>
</ul>
</br>
</br>
</br>


<h1 id="hexagonal-architectureports-and-adapters">Hexagonal Architecture(ports-and-adapters)</h1>
<p><img src="https://velog.velcdn.com/images/ilov-/post/6f6a72f9-e507-4b97-b670-26dd5c391f04/image.png" alt=""></p>
<blockquote>
<p>The idea of Hexagonal Architecture is to put inputs and outputs at the edges of our design. Business logic should not depend on whether we expose a REST or a GraphQL API, and it should not depend on where we get data from — a database, a microservice API exposed via gRPC or REST, or just a simple CSV file.</p>
</blockquote>
<p>-<a href="https://netflixtechblog.com/ready-for-changes-with-hexagonal-architecture-b315ec967749">Netflix Technology Blog</a></p>
<ul>
<li><p>비즈니스 로직을 설계의 중심으로 하면서,
노출시키는 영역(outputs)과 데이터를 가져오는 영역(inputs)에 의존하지 않는 디자인을 의미</p>
</li>
<li><p>의존성 역전을 통해 비즈니스 로직이 외부 기술로부터 완전히 독립됨</p>
</li>
<li><p>구조: 핵심 Domain(중심), Usecase ↔ Port(인터페이스) ↔ Adapter(구현체)</p>
</li>
<li><p>장점</p>
<ul>
<li>테스트 용이성: DB나 외부 API 없이도 비즈니스 로직만 따로 떼어 모킹(Mocking) 테스트하기 매우 좋음</li>
<li>기술 독립성: Java 버전을 올리거나, DB를 MySQL에서 MongoDB로 바꿔도 핵심 로직은 건드릴 필요가 없음</li>
<li>유지보수: 각 기능이 포트와 어댑터로 명확히 분리되어 있어 영향 범위 파악이 쉬움<h3 id="process">process</h3>
</li>
</ul>
</li>
<li><p><em>1. Inside: 애플리케이션 핵심 (Core)*</em></p>
</li>
<li><p>가장 안쪽에는 Domain Entities와 Use Cases가 존재</p>
</li>
<li><p><code>Domain</code>: 비즈니스 규칙의 정수. Java의 순수 객체(POJO)로 작성되며 외부 라이브러리에 의존하지 않음</p>
</li>
<li><p><code>Use Case</code>: 애플리케이션의 핵심 비즈니스 로직을 수행하는 최소 단위의 실행 흐름을 의미. 입력 포트를 구현하여 비즈니스 흐름을 제어</p>
</li>
</ul>
<p><strong>2. Boundary: 포트 (Ports)</strong></p>
<ul>
<li>핵심 로직과 외부 세계를 연결하는 인터페이스</li>
<li>Input Port (Inbound): 외부에서 내부로 들어오는 요청을 위한 명세 (예: RegisterUserUseCase)</li>
<li>Output Port (Outbound): 내부에서 외부로 나가는 요청을 위한 명세 (예: LoadUserPort, SaveUserPort)</li>
</ul>
<p><strong>3. Outside: 어댑터 (Adapters)</strong></p>
<ul>
<li>포트를 통해 실제로 외부와 통신하는 구현체</li>
<li>Driving Adapter (Inbound): 사용자의 요청을 변환하여 입력 포트를 호출 (예: UserController, MessageQueueListener)</li>
<li>Driven Adapter (Outbound): 출력 포트를 구현하여 실제 인프라와 통신 (예: UserJpaAdapter, ExternalMailAdapter)</li>
</ul>
</br>
</br>

<h3 id="구현-순서-및-코드-예시">구현 순서 및 코드 예시</h3>
<ul>
<li>중심부부터 바깥쪽으로 작성하는 것이 원칙</li>
</ul>
<ol>
<li>Domain: 순수 비즈니스 객체</li>
<li>Usecase (Port): 비즈니스 실행 단위 인터페이스</li>
<li>Adapter (Outbound): DB 저장 등 외부 구현체</li>
<li>Adapter (Inbound): 컨트롤러 등 외부 유입 지점</li>
</ol>
</br>

<p><strong>코드 예시</strong></p>
<pre><code class="language-java">// 1. Domain (순수 java 객체)
public class User {
    private Long id;
    private String name;

    public User(String name) {
        this.name = name;
    }
    // Getter, 비즈니스 로직 등...
}

// 2. Usecase &amp; Port (interface)
// Inbound Port: 외부(Controller)에서 내부로 들어오는 관문
public interface RegisterUserUseCase {
    void register(String name);
}

// Outbound Port: 내부에서 외부(DB)로 나가는 관문
public interface SaveUserPort {
    void save(User user);
}

// Usecase Imolementaion (service)
@Service
@RequiredArgsConstructor
public class RegisterUserService implements RegisterUserUseCase {

    private final SaveUserPort saveUserPort; // Outbound Port 주입

    @Override
    public void register(String name) {
        User user = new User(name);
        // 비즈니스 검증 로직 등이 위치함
        saveUserPort.save(user);
    }
}

// Adapters
// 3. Outbound Adapter (Persistence)&#39;
@Component
@RequiredArgsConstructor
public class UserPersistenceAdapter implements SaveUserPort {
    private final UserJpaRepository repository; // 실제 Spring Data JPA 사용

    @Override
    public void save(User user) {
        // Domain 객체를 Entity로 변환하여 저장
        repository.save(new UserEntity(user.getName()));
    }
}

// DB Entity
package com.example.adapter.out.persistence;

import jakarta.persistence.*;

@Entity
@Table(name = &quot;users&quot;)
public class UserJpaEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    // JPA를 위한 기본 생성자 및 매핑용 메서드들...
    public User toDomain() { // DB 엔티티를 도메인 객체로 변환
        return new User(this.id, this.name);
    }
}

// 4. Inbound Adapter (web)
@RestController
@RequiredArgsConstructor
public class UserController {
    private final RegisterUserUseCase registerUserUseCase; // Port 호출

    @PostMapping(&quot;/users&quot;)
    public void register(@RequestParam String name) {
        registerUserUseCase.register(name);
    }
}</code></pre>
</br>

<p><strong>전체 경로</strong></p>
<pre><code class="language-xml">src
└── main
    └── java
        └── com.example
            ├── domain (핵심 비즈니스)
            │   └── User.java                          &lt;-- [순수 Domain Entity]
            │
            ├── application (비즈니스 로직 조립)
            │   ├── port
            │   │   ├── in
            │   │   │   └── RegisterUserUseCase.java   &lt;-- [Interface]
            │   │   └── out
            │   │       └── SaveUserPort.java          &lt;-- [Interface]
            │   └── service
            │       └── RegisterUserService.java       &lt;-- [UseCase 구현체]
            │
            └── adapter (외부 기술 연결)
                ├── in 
                │   └── web
                │       └── UserController.java        &lt;-- [Spring MVC Controller]
                └── out
                    └── persistence
                        ├── UserJpaEntity.java         &lt;-- [DB Entity (JPA)]
                        ├── UserJpaRepository.java     &lt;-- [Spring Data JPA]
                        └── UserPersistenceAdapter.java &lt;-- [Port 구현체]

// 간략
src
└── main
    └── java
        └── com.example
            ├── domain
            ├── application
            │   ├── port
            │   │   ├── in            &lt;-- UseCase 인터페이스 위치
            │   │   └── out           &lt;-- Persistence Port 인터페이스 위치
            │   └── service           &lt;-- [UseCase Impl]
            └── adapter
                ├── in
                │   └── web           &lt;-- Controller 위치
                └── out
                    └── persistence   &lt;-- DB Entity, JPA Repository 위치               
</code></pre>
</br>
</br>

<h3 id="reference">reference</h3>
<blockquote>
<p><a href="https://gngsn.tistory.com/258#google_vignette">https://gngsn.tistory.com/258#google_vignette</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Kotlin] 후행 람다(Trailing Lambda)]]></title>
            <link>https://velog.io/@ilov-/Kotlin-%ED%9B%84%ED%96%89-%EB%9E%8C%EB%8B%A4Trailing-Lambda</link>
            <guid>https://velog.io/@ilov-/Kotlin-%ED%9B%84%ED%96%89-%EB%9E%8C%EB%8B%A4Trailing-Lambda</guid>
            <pubDate>Tue, 06 Jan 2026 06:22:33 GMT</pubDate>
            <description><![CDATA[<h2 id="후행-람다-trailing-lambda">후행 람다 (Trailing Lambda)</h2>
<ul>
<li><p><code>함수의 마지막 인자가 함수(람다)인 경우, 소괄호 밖으로 뺄 수 있다</code>라는 규칙이 있음</p>
<ul>
<li>규칙 1: 마지막 인자가 함수면 ) 뒤에 { }를 붙여서 쓸 수 있음.</li>
<li>규칙 2: 인자가 두 개 이상일 때, 마지막 람다만 밖으로 나감.</li>
</ul>
</li>
</ul>
<pre><code>     이 문법을 사용하면 마치 새로운 예약어(Keyword)를 만든 것처럼 코드를 읽을 수 있음.
        if (condition) { ... }

        for (item in list) { ... }

        executeWithLock(lock) { ... } &lt;- **&quot;이 락을 가지고 이 로직을 실행해!&quot;**라고 훨씬 직관적으로 읽힘
</code></pre><ul>
<li><p>예)</p>
<pre><code class="language-kotlin">fun executeWithLock(lockProp: RedisKeyProperties.Lock,
                      exec: () -&gt; Unit) {
    val lock:RLock = redissonClient.getLock(lockProp.key)
    val timeUnit = TimeUnit.MILLISECONDS

    val waitTime = timeUnit.convert(lockProp.waitTime.value, lockProp.waitTime.timeUnit)
    val leaseTime = timeUnit.convert(lockProp.leaseTime.value, lockProp.leaseTime.timeUnit)

    if (lock.tryLock(waitTime, leaseTime, timeUnit)) {
        try {
            logger.info(&quot;${lockProp.key} is locked&quot;)
            exec()
        } finally {
            lock.unlock()
            logger.info(&quot;${lockProp.key} is released&quot;)
        }
    } else {
        logger.info(&quot;${lockProp.key} can not get lock&quot;)
    }
}


</code></pre>
</li>
</ul>
<p>// 실행하려는 곳에서 (Trailing Lambda 사용)
executeWithLock(redisKeyProperties.segmentMappingLock) {
    metaMappingService.deleteMapping(request)
    executed = true
}</p>
<p>// 이 전체가 하나의 &#39;exec&#39; 함수가 됨
{
    metaMappingService.deleteMapping(request) // 첫 번째 실행문
    executed = true                           // 두 번째 실행문 (외부의 변수까지 변경가능)
}</p>
<p>```</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Hibernate] Hibernate가 관리하는 컬렉션의 참조(Reference)]]></title>
            <link>https://velog.io/@ilov-/Hibernate-Hibernate%EA%B0%80-%EA%B4%80%EB%A6%AC%ED%95%98%EB%8A%94-%EC%BB%AC%EB%A0%89%EC%85%98%EC%9D%98-%EC%B0%B8%EC%A1%B0Reference</link>
            <guid>https://velog.io/@ilov-/Hibernate-Hibernate%EA%B0%80-%EA%B4%80%EB%A6%AC%ED%95%98%EB%8A%94-%EC%BB%AC%EB%A0%89%EC%85%98%EC%9D%98-%EC%B0%B8%EC%A1%B0Reference</guid>
            <pubDate>Wed, 24 Dec 2025 02:24:35 GMT</pubDate>
            <description><![CDATA[<h3 id="기존-코드와-문제점">기존 코드와 문제점</h3>
<ul>
<li>기존 코드</li>
</ul>
<pre><code class="language-java">if (!isNewGroup) {
    group.groupAttachFiles = groupAttachFileRepository.findByGroup(group.id)
        .toMutableList()
}

// Hibernate가 관리하는 컬렉션(PersistentBag)을 일반 List로 덮어씌워서 연결이 끊어짐</code></pre>
<ul>
<li><p>문제점</p>
<ul>
<li>Hibernate가 관리하는 컬렉션의 참조(Reference)를 끊어버림.</li>
<li>Hibernate는 엔티티를 조회할 때 List나 Set 같은 컬렉션을 PersistentBag 이라는 특수한 래퍼(Wrapper) 객체로 감싸서 관리함</li>
<li>이 객체는 변경 사항(추가, 삭제)을 감지하여 DB에 반영하는 역할을 힘
하지만 코드에서 campaign.campaignAttachFiles = ... 로 새로운 리스트를 대입하면, Hibernate가 감시하던 PersistentBag이 버려지고 일반 ArrayList로 교체되어버림 <ul>
<li>이 순간 Hibernate는 <strong>&quot;관리하던 자식 객체들의 연결이 끊어졌다&quot;</strong>고 판단하여 에러를 발생시킴</li>
</ul>
</li>
</ul>
</li>
</ul>
<h3 id="해결-방안">해결 방안</h3>
<pre><code class="language-java">// DB의 값을 미리 가져옴
val groupAttachFiles = groupAttachFileRepository.findByGroup(group.id)

// 수정 시, 항상 DB의 데이터를 반영하도록 한다.
val isNewGroup = group.id == 0L
if (!isNewGroup) {
   group.groupAttachFiles.clear()        // 기존 리스트를 비움 (참조 유지) (delete-orphan 동작)
   groupAttachFiles.forEach {
        group.groupnAttachFiles.add(it)  // 가져온 파일들을 채워 넣음
   }
}</code></pre>
<ul>
<li>리스트 자체를 교체해버리지 말고, 기존 리스트의 내용을 비우고(clear) 새로 채워 넣는(addAll) 방식으로 변경해야 함</li>
</ul>
<h3 id="더-알아보기">더 알아보기</h3>
<ul>
<li><p>영속성 컨텍스트와 컬렉션 래퍼 (Persistent Collection)
핵심: Hibernate가 엔티티의 List, Set을 어떻게 PersistentBag, PersistentSet으로 감싸는지(Proxy) 이해해야 함</p>
<ul>
<li>포인트: &quot;왜 내 리스트의 클래스 타입을 찍어보면 java.util.ArrayList가 아니라 org.hibernate.collection...이 나올까?&quot;</li>
</ul>
</li>
<li><p>고아 객체 제거 (Orphan Removal) vs Cascade.REMOVE
핵심: orphanRemoval = true 옵션의 정확한 동작 원리.</p>
<ul>
<li>포인트 : list.remove(entity)를 했을 때 DB에서 DELETE 쿼리가 나가는 원리. 왜 컬렉션을 통째로 교체하면(=) Hibernate가 이를 &quot;모든 자식을 고아로 만들었다&quot;고 오해하거나 에러를 뱉는지.</li>
</ul>
</li>
<li><p>변경 감지 (Dirty Checking)와 Merge
핵심: save()를 호출하지 않아도 트랜잭션이 끝날 때 UPDATE 쿼리가 나가는 원리.</p>
<ul>
<li>포인트: JPA에서 데이터를 수정할 때 왜 setter만 호출하고 save를 부르지 않아도 되는지, <pre><code>    그리고 컬렉션 내부의 변경은 어떻게 감지되는지.</code></pre></li>
</ul>
</li>
<li><p>연관 관계 편의 메서드 (Helper Method)
핵심: 양방향 연관 관계(부모 ↔ 자식)에서 실수하기 쉬운 부분을 방지하는 패턴.</p>
<ul>
<li>포인트: setParent(this)와 list.add(child)를 묶어서 하나의 메서드로 관리하는 방법 (예: campaign.addFile(file)).</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Algorithm] maximum-sunarray (카데인 알고리즘)]]></title>
            <link>https://velog.io/@ilov-/maximum-subarray</link>
            <guid>https://velog.io/@ilov-/maximum-subarray</guid>
            <pubDate>Thu, 17 Apr 2025 01:36:06 GMT</pubDate>
            <description><![CDATA[<h2 id="문제">문제</h2>
<pre><code class="language-html"># Given an integer array nums, find the subarray with the largest sum,
  and return its sum.


Example 1:
Input: nums = [-2,1,-3,4,-1,2,1,-5,4]
Output: 6
Explanation: The subarray [4,-1,2,1] has the largest sum 6.


Constraints:
1 &lt;= nums.length &lt;= 10^5
-10^4 &lt;= nums[i] &lt;= 10^4
</code></pre>
</br>

<h2 id="해결-방법">해결 방법</h2>
<h3 id="1-brute-force">1. Brute force</h3>
<ul>
<li><p>시간 복잡도 : O(N²)</p>
</li>
<li><p>배열의 모든 값들을 더해보고 그 중 가장 큰 것 반환</p>
<pre><code class="language-java">public int maxSubArray(int[] nums) {

  int maxSum = Integer.MIN_VALUE;

  for (int i = 0; i &lt; nums.length; i++) {
       int curSum = 0;
       for (int j = i; j &lt; nums.length; j++) {
           curSum += nums[j];
           maxSum = Math.max(curSum, maxSum);
       }
   }
   return maxSum;
}</code></pre>
<h3 id="2-kadanes-algorithm">2. Kadane&#39;s Algorithm</h3>
</li>
<li><p>Dynamic Programming</p>
<ul>
<li>문제를 여러 개의 하위 문제로 나누어 푼 다음 그것을 결합하여 최종 목적에 도달하는 것</li>
<li>한번 푼 문제는 어딘가에 저장해두고 반복하여 풀지 않음</li>
</ul>
</li>
<li><p>카데인 알고리즘</p>
<ul>
<li><p>연속 부분 배열의 최대 합을 구하는 효율적인 방법</p>
</li>
<li><p>시간 복잡도 O(N)</p>
</li>
<li><p>DP 접근법 사용</p>
</li>
<li><p>원리 </p>
<ul>
<li>카데인 알고리즘은 현재까지의 최대 부분 배열의 합을 유지하면서, 각 요소를 순차적으로 탐색합니다. </li>
<li>알고리즘은 두 가지 값을 유지합니다.<blockquote>
<p>현재까지의 최대 합 (max_so_far) : 지금까지 발견한 최대 부분 배열의 합 
현재 위치에서 끝나는 최대 합 (max_ending_here) : 현재 위치에서 끝나는 부분 배열 중 최대 합</p>
</blockquote>
</li>
</ul>
<pre><code class="language-java">public int maxSubArray(int[] nums) {
  int curSum = nums[0];
  int maxSum = nums[0];

  for (int i = 1; i &lt; nums.length; i++) {
      curSum = Math.max(nums[i], curSum + nums[i]);  // 현재 요소를 포함한 최대 합
      maxSum = Math.max(maxSum, curSum);  // 현재까지의 최대 합으로 갱신
  }

  return maxSum;
}

</code></pre>
</li>
</ul>
</li>
</ul>
<h3 id="3-정리">3. 정리</h3>
<ul>
<li>분할 정복도 고려 가능</li>
</ul>
<table>
<thead>
<tr>
<th>방법</th>
<th>시간 복잡도</th>
<th>공간 복잡도</th>
</tr>
</thead>
<tbody><tr>
<td>Brute Force</td>
<td>O(n²)</td>
<td>O(1)</td>
</tr>
<tr>
<td>Kadane (최적)</td>
<td>O(n)</td>
<td>O(1)</td>
</tr>
<tr>
<td>Divide &amp; Conquer</td>
<td>O(n log n)</td>
<td>O(log n)</td>
</tr>
</tbody></table>
</br>
</br>

<h3 id="reference">reference</h3>
<blockquote>
<p><a href="https://medium.com/@vdongbin/kadanes-algorithm-%EC%B9%B4%EB%8D%B0%EC%9D%B8-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-acbc8c279f29">https://medium.com/@vdongbin/kadanes-algorithm-%EC%B9%B4%EB%8D%B0%EC%9D%B8-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-acbc8c279f29</a>
<a href="https://fubabaz.tistory.com/65">https://fubabaz.tistory.com/65</a> - 카데인 알고리즘</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Kotlin] logging]]></title>
            <link>https://velog.io/@ilov-/FTP</link>
            <guid>https://velog.io/@ilov-/FTP</guid>
            <pubDate>Thu, 20 Mar 2025 09:06:04 GMT</pubDate>
            <description><![CDATA[<h2 id="klogging-klogger-kotlinlogging">KLogging, KLogger, KotlinLogging</h2>
<pre><code class="language-kotlin">//
import mu.KLogging

class MyClass {
    companion object : KLogging()

    fun doSomething() {
        logger.info { &quot;This is a log message from MyClass&quot; }
    }
}

//
import mu.KLogger
import mu.KotlinLogging

class MyClass {
    private val logger: KLogger = KotlinLogging.logger {}

    fun doSomething() {
        logger.info { &quot;This is a log message from MyClass&quot; }
    }
}

//
import mu.KotlinLogging

private val logger = KotlinLogging.logger {}

fun main() {
    logger.info { &quot;This is a log message from main function&quot; }
}


</code></pre>
<table>
<thead>
<tr>
<th></th>
<th>KLogging</th>
<th>KLogger</th>
<th>KotlinLogging</th>
</tr>
</thead>
<tbody><tr>
<td>설명</td>
<td>클래스 레벨 로깅을 위한 싱글톤</td>
<td>로거 인터페이스</td>
<td>KLogger 객체를 생성하는 유틸</td>
</tr>
<tr>
<td>사용 방식</td>
<td>companion object : KLogging()</td>
<td>val logger: KLogger = KotlinLogging.logger {}</td>
<td>val logger = KotlinLogging.logger {}</td>
</tr>
<tr>
<td>주요 차이점</td>
<td><code>logger</code> 변수를 자동으로 제공</td>
<td>인터페이스로 직접 사용해야 함</td>
<td>KLogger 객체를 쉽게 생성하는 유틸</td>
</tr>
</tbody></table>
</br>
</br>

<h3 id="언제-어떤-걸-써야-할까">언제 어떤 걸 써야 할까?</h3>
<ul>
<li>클래스에서 로깅을 사용할 때:
→ companion object : KLogging() 사용 (편리함)</li>
<li>로깅을 수동으로 관리하고 싶을 때:
→ KLogger를 직접 생성하여 사용</li>
<li>파일 내에서 전역적으로 로깅을 사용할 때:
→ val logger = KotlinLogging.logger {} 사용</li>
</ul>
</br>

<h3 id="kotlin에서는-자바와-달리-slf4j를-사용하지-않는-이유">Kotlin에서는 자바와 달리 @Slf4j를 사용하지 않는 이유</h3>
<ul>
<li>Lombok이 Kotlin과 완벽하게 맞지 않음</li>
<li>Kotlin의 object와 companion object로 싱글턴 패턴을 쉽게 구현 가능</li>
<li>KotlinLogging은 성능 최적화(람다 방식) 지원</li>
<li>설정이 필요 없고 더 깔끔한 코드 가능</li>
</ul>
</br>
</br>
</br>


<h3 id="reference">Reference</h3>
<blockquote>
<p><a href="https://github.com/klogging/klogging">https://github.com/klogging/klogging</a>
<a href="https://oimbook.tistory.com/entry/Kotlin-%EB%8B%B5%EA%B2%8C-%EA%B0%84%EB%8B%A8%ED%95%98%EA%B2%8C-%EB%A1%9C%EA%B9%85%ED%95%B4%EB%B3%B4%EA%B8%B0">https://oimbook.tistory.com/entry/Kotlin-%EB%8B%B5%EA%B2%8C-%EA%B0%84%EB%8B%A8%ED%95%98%EA%B2%8C-%EB%A1%9C%EA%B9%85%ED%95%B4%EB%B3%B4%EA%B8%B0</a>
<a href="https://lannstark.tistory.com/141">https://lannstark.tistory.com/141</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Java] Reflection, Class 객체]]></title>
            <link>https://velog.io/@ilov-/Reflection-Class-%EA%B0%9D%EC%B2%B4</link>
            <guid>https://velog.io/@ilov-/Reflection-Class-%EA%B0%9D%EC%B2%B4</guid>
            <pubDate>Mon, 10 Mar 2025 02:12:32 GMT</pubDate>
            <description><![CDATA[<h1 id="class-객체">Class 객체</h1>
<pre><code class="language-java">package java.lang

public final class Class&lt;T&gt; implements Serializable, GenericDeclaration, Type, AnnotatedElement {

    private final ClassLoader classLoader;

     private Class(ClassLoader loader) {
        classLoader = loader;
    }


}</code></pre>
<blockquote>
<p>Instances of the class Class represent classes and interfaces in a running Java application. 
An enum class and a record class are kinds of class; an annotation interface is a kind of interface. 
Every array also belongs to a class that is reflected as a Class object that is shared by all arrays with the same element type and number of dimensions. 
The primitive Java types (boolean, byte, char, short, int, long, float, and double), and the keyword void are also represented as Class objects.</p>
</blockquote>
<p>Class has no public constructor. Instead a Class object is constructed automatically by the Java Virtual Machine when a class is derived from the bytes of a class file through the invocation of one of the following methods:</p>
<ul>
<li><p>java에서 클래스와 인터페이스는 런타임 중에 <code>Class 객체</code>로 표현됨
즉, 모든 클래스와 인터페이스는 <code>Class 객체</code>를 통해 메타데이터를 제공    </p>
<ul>
<li>enum, record, annotation 도 <code>Class 객체</code>로 표현됨 </li>
<li>모든 배열도 내부적으로 Class 객체를 공유 (같은 타입과 차원을 가지면 동일한 Class 객체 공유)</li>
<li>기본 타입(Primitive Type)도 <code>Class 객체</code>로 표현됨 (int, double, boolean 등)</li>
</ul>
</li>
<li><p>생성자를 통해 만들 수 없고 JVM이 자동으로 생성해 줌</p>
</li>
<li><p>리플렉션이나 동적 클래스 생성에서 사용될 수 있음</p>
</li>
</ul>
</br>
</br>

<h1 id="reflection">Reflection</h1>
<ul>
<li><p>Java에서 런타임 중에 클래스, 메서드, 필드 등을 조사하고 조작할 수 있는 기능</p>
</li>
<li><p>컴파일 시점이 아니라 실행 시점(Run-time)에 클래스 정보를 가져오거나 수정 가능</p>
</li>
<li><p>Class 객체 사용</p>
<ul>
<li>클래스 정보 가져오기</li>
<li>객체 동적 생성하기</li>
<li>메서드 실행하기</li>
<li>필드 값 읽기/수정하기</li>
</ul>
</li>
</ul>
<h3 id="1-클래스-정보-가져오기">1. 클래스 정보 가져오기</h3>
<ul>
<li>예시</li>
</ul>
<pre><code class="language-java">class Person {
    private String name;
    public int age;

    public Person() {}

    public void sayHello() {
        System.out.println(&quot;Hello!&quot;);
    }
}

public class ReflectionExample {
    public static void main(String[] args) {
        try {
            // ① Class 객체 얻기 (3가지 방법)
            Class&lt;?&gt; clazz1 = Person.class;  // 정적 방식
            Class&lt;?&gt; clazz2 = new Person().getClass(); // 인스턴스를 통해 얻기
            Class&lt;?&gt; clazz3 = Class.forName(&quot;Person&quot;); // 문자열을 통해 얻기

            // ② 클래스의 필드 목록 가져오기
            Field[] fields = clazz1.getDeclaredFields();
            System.out.println(&quot;Fields:&quot;);
            for (Field field : fields) {
                System.out.println(&quot;- &quot; + field.getName());
            }

            // ③ 클래스의 메서드 목록 가져오기
            Method[] methods = clazz1.getDeclaredMethods();
            System.out.println(&quot;\nMethods:&quot;);
            for (Method method : methods) {
                System.out.println(&quot;- &quot; + method.getName());
            }

            // ④ 클래스의 생성자 목록 가져오기
            Constructor&lt;?&gt;[] constructors = clazz1.getConstructors();
            System.out.println(&quot;\nConstructors:&quot;);
            for (Constructor&lt;?&gt; constructor : constructors) {
                System.out.println(&quot;- &quot; + constructor.getName());
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
</code></pre>
<h3 id="2-활용">2. 활용</h3>
<ul>
<li>프레임워크(Spring, Hibernate 등)
→ @Autowired 같은 기능은 리플렉션을 사용해 동적으로 의존성을 주입함.</li>
<li>JSON 직렬화/역직렬화
→ Jackson, Gson 같은 라이브러리는 리플렉션을 사용해 필드 값을 자동으로 변환함.</li>
<li>테스트 프레임워크(JUnit)
→ @Test 메서드를 찾아 실행하는 것도 리플렉션을 이용함.</li>
</ul>
<h3 id="3-단점">3. 단점</h3>
<ul>
<li>성능 저하
→ 일반적인 new보다 객체 생성이 느림.</li>
<li>보안 문제
→ private 필드까지 접근할 수 있어서 보안 취약점이 발생할 가능성이 있음.</li>
<li>코드 가독성 저하
→ 일반 코드보다 복잡하고, 유지보수가 어려움.</li>
<li>그래서 필요할 때만 최소한으로 사용하자!</li>
</ul>
</br>
</br>
</br>

<h3 id="reference">reference</h3>
<blockquote>
<p><a href="https://docs.oracle.com/en/java/javase/23/docs/api/java.base/java/lang/Class.html">https://docs.oracle.com/en/java/javase/23/docs/api/java.base/java/lang/Class.html</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Java] equals(), hashCode(), toString(), @EqualsAndHashCode(), contains()]]></title>
            <link>https://velog.io/@ilov-/Java-equals-hashCode-toString-EqualsAndHashCode-contains</link>
            <guid>https://velog.io/@ilov-/Java-equals-hashCode-toString-EqualsAndHashCode-contains</guid>
            <pubDate>Fri, 17 Jan 2025 06:25:47 GMT</pubDate>
            <description><![CDATA[<h1 id="object-클래스">Object 클래스</h1>
<pre><code class="language-java">
public class Object {

    public boolean equals(Object obj) {
        return (this == obj);
    }
    // 두 객체의 같고 다름을 참조변수의 주소값으로 판단

    public native int hashCode();
    // 해시함수는 찾고자하는 값을 입력하면, 그 값이 저장된 위치를 알려주는 해시코드를 반환

    public String toString() {
        return getClass().getName() + &quot;@&quot; + Integer.toHexString(hashCode());
    }
    // 클래스이름에 16진수의 해시코드

}

public class Arrays {

    public static int hashCode(Object a[]) {
        if (a == null)
            return 0;

        int result = 1;

        for (Object element : a)
            result = 31 * result + (element == null ? 0 : element.hashCode());

        return result;
    }
}
</code></pre>
</br>

<h3 id="equals">equals()</h3>
<ul>
<li>Object 클래스로부터 상속받은 equals 메서드는 결국 두 개의 참조변수가 같은 객체를 참조하고 있는지, 즉 두 참조변수에 저장된 주소값이 같은지를 판단하는 기능만 가능 </li>
<li>equals 메서드로 value 값을 비교하려면 오바라이딩해야 함</li>
</ul>
<blockquote>
<p>String.equals는 가지고 있는 값이 동일하면 TRUE
String == 연산자는 주소값을 비교</p>
<p>하지만, Object 클래스에서의 equals는 equality is the same as object identity, 즉. 객체 동일성이다.</p>
</blockquote>
<pre><code class="language-java">    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        TestObject that = (TestObject) o;
        return id == that.id &amp;&amp; Objects.equals(name, that.name);
    }
</code></pre>
</br>

<h3 id="hashcode">hashCode()</h3>
<ul>
<li>해시함수는 찾고자하는 값을 입력하면, 그 값이 저장된 위치를 알려주는 해시코드를 반환</li>
<li>해싱 알고리즘에 의해 반환된 정수 값</li>
<li>HashMap과 같은 자료 구조에서 사용되는 Hash Table에서의 hashing을 가능하게 하는 것을 목적으로 함</li>
<li>equals 비교에 사용되는 정보가 변경되지 않았다면, 어플리케이션이 실행되는 동안 그 객체의 hashCode 메소드는 몇 번을 호출해도 일관되게 항상 같은 값을 반환해야 한다.(단, 애플리케이션을 다시 실행한다면 이 값이 달라져도 상관없다.)</li>
<li>equals(Object)가 두 객체를 같다고 판단했다면, hashCode는 똑같은 값을 반환해야 한다.</li>
<li>equals(Object)가 두 객체를 다르다고 판단했더라도, hashCode가 서로 다른 값을 반환할 필요는 없다. 단, 다른 객체에 대해서는 다른 값을 반환해야 해시테이블의 성능이 좋아진다.</li>
<li>HashCode는 객체의 주소값이 아니라, 내부 요소와 값을 다루며 그 값에 따라 HashCode 값도 바뀜<pre><code class="language-java">@Override
public int hashCode() {
  int hash = 7;
  hash = 31 * hash + (int) id;
  hash = 31 * hash + (name == null ? 0 : name.hashCode());
  hash = 31 * hash + (email == null ? 0 : email.hashCode());
  return hash;
}</code></pre>
</li>
</ul>
</br>

<h3 id="tostring">toString()</h3>
<ul>
<li>인스턴스에 대한 정보를 문자열(String)로 제공할 목적으로 정의한 것<pre><code class="language-java">  @Override
  public String toString() {
      return &quot;TestObject{&quot; +
          &quot;id=&quot; + id +
          &quot;, name=&#39;&quot; + name + &#39;\&#39;&#39; +
          &#39;}&#39;;
  }</code></pre>
</li>
</ul>
</br>

<h3 id="equalsandhashcode">@EqualsAndHashCode()</h3>
<ul>
<li>클래스 위에 정의하면, 컴파일 시점에 자동으로 객체의 필드로부터 HashCode와 Equals를 오버라이딩하여 구현해주는 애노테이션</li>
<li>클래스에 있는 모든 필드들에 대한 비교를 수행함</li>
<li><a href="https://velog.io/@user1/EqualsAndHashCode-%EC%9D%98-%EC%98%B5%EC%85%98">옵션</a></li>
</ul>
</br>

<h3 id="contains">contains()</h3>
<pre><code class="language-java">public class ArrayList&lt;E&gt; extends AbstractList&lt;E&gt; implements List&lt;E&gt;, RandomAccess, Cloneable, java.io.Serializable {

    public boolean contains(Object o) {
        return indexOf(o) &gt;= 0;
    }

    /**
     * Returns the index of the first occurrence of the specified element
     * in this list, or -1 if this list does not contain the element.
     * More formally, returns the lowest index {@code i} such that
     * {@code Objects.equals(o, get(i))},
     * or -1 if there is no such index.
     */
    public int indexOf(Object o) {
        return indexOfRange(o, 0, size);
    }

    int indexOfRange(Object o, int start, int end) {
        Object[] es = elementData;
        if (o == null) {
            for (int i = start; i &lt; end; i++) {
                if (es[i] == null) {
                    return i;
                }
            }
        } else {
            for (int i = start; i &lt; end; i++) {
                if (o.equals(es[i])) {
                    return i;
                }
            }
        }
        return -1;
    }
}
</code></pre>
</br>

<h3 id="containsall">containsAll()</h3>
<pre><code class="language-java">public abstract class AbstractList&lt;E&gt; extends AbstractCollection&lt;E&gt; implements List&lt;E&gt; {
    /**
     * Sole constructor.  (For invocation by subclass constructors, typically
     * implicit.)
     */
    protected AbstractList() {
    }

    public boolean containsAll(Collection&lt;?&gt; c) {
        for (Object e : c)
            if (!contains(e))
                return false;
        return true;
    }

    public boolean contains(Object o) {
        Iterator&lt;E&gt; it = iterator();
        if (o==null) {
            while (it.hasNext())
                if (it.next()==null)
                    return true;
        } else {
            while (it.hasNext())
                if (o.equals(it.next()))
                    return true;
        }
        return false;
    }
}

</code></pre>
<h2 id="equals와-hashcode를-재정의해야하는-경우">equals()와 hashCode()를 재정의해야하는 경우</h2>
<table>
<thead>
<tr>
<th>경우</th>
<th>equals()와 hashCode() 필요 여부</th>
<th>비고</th>
</tr>
</thead>
<tbody><tr>
<td>HashSet, HashMap, HashTable 사용</td>
<td>✅ 필요</td>
<td></td>
</tr>
<tr>
<td>contains(), containsKey() 사용</td>
<td>✅ 필요</td>
<td></td>
</tr>
<tr>
<td>TreeSet, TreeMap 사용</td>
<td>✅ 필요 (Comparable과 함께)</td>
<td></td>
</tr>
<tr>
<td>객체의 참조 값 비교 (각 객체가 고유해야 함)</td>
<td>❌ 불필요</td>
<td></td>
</tr>
<tr>
<td>Enum 타입</td>
<td>❌ 불필요</td>
<td></td>
</tr>
</tbody></table>
</br>
</br>
</br>
</br>

<h3 id="reference">reference</h3>
<blockquote>
<p><a href="https://www.baeldung.com/java-hashcode#standard-hashcode-implementations">https://www.baeldung.com/java-hashcode#standard-hashcode-implementations</a> 
<a href="https://velog.io/@jyleedev/EqualsAndHashCode-%EA%B7%B8%EB%A6%AC%EA%B3%A0-HashCode%EC%99%80-Equals">https://velog.io/@jyleedev/EqualsAndHashCode-%EA%B7%B8%EB%A6%AC%EA%B3%A0-HashCode%EC%99%80-Equals</a>
<a href="https://velog.io/@user1/EqualsAndHashCode-%EC%9D%98-%EC%98%B5%EC%85%98">https://velog.io/@user1/EqualsAndHashCode-%EC%9D%98-%EC%98%B5%EC%85%98</a>
<a href="https://velog.io/@injoon2019/%EC%9E%90%EB%B0%94-equals-hashCode-toString">https://velog.io/@injoon2019/%EC%9E%90%EB%B0%94-equals-hashCode-toString</a>
<a href="https://wyatti.tistory.com/entry/Java-List-Contains-%EB%A9%94%EC%86%8C%EB%93%9C-%EC%99%84%EB%B2%BD-%EA%B0%80%EC%9D%B4%EB%93%9C-%EC%A0%95%EC%9D%98-%EC%82%AC%EC%9A%A9%EB%B2%95-%EC%98%88%EC%A0%9C-%EC%BD%94%EB%93%9C">contains()</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[아파치 스파크]]></title>
            <link>https://velog.io/@ilov-/%EC%95%84%ED%8C%8C%EC%B9%98-%EC%8A%A4%ED%8C%8C%ED%81%AC</link>
            <guid>https://velog.io/@ilov-/%EC%95%84%ED%8C%8C%EC%B9%98-%EC%8A%A4%ED%8C%8C%ED%81%AC</guid>
            <pubDate>Fri, 27 Dec 2024 02:49:56 GMT</pubDate>
            <description><![CDATA[<p><a href="https://artist-developer.tistory.com/7">https://artist-developer.tistory.com/7</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JAVA] 고급 1편 (멀티스레드와 동시성) 5 - 메모리 가시성]]></title>
            <link>https://velog.io/@ilov-/JAVA-%EA%B3%A0%EA%B8%89-1%ED%8E%B8-%EB%A9%80%ED%8B%B0%EC%8A%A4%EB%A0%88%EB%93%9C%EC%99%80-%EB%8F%99%EC%8B%9C%EC%84%B1-%EB%A9%94%EB%AA%A8%EB%A6%AC-%EA%B0%80%EC%8B%9C%EC%84%B1</link>
            <guid>https://velog.io/@ilov-/JAVA-%EA%B3%A0%EA%B8%89-1%ED%8E%B8-%EB%A9%80%ED%8B%B0%EC%8A%A4%EB%A0%88%EB%93%9C%EC%99%80-%EB%8F%99%EC%8B%9C%EC%84%B1-%EB%A9%94%EB%AA%A8%EB%A6%AC-%EA%B0%80%EC%8B%9C%EC%84%B1</guid>
            <pubDate>Sun, 01 Dec 2024 07:38:32 GMT</pubDate>
            <description><![CDATA[<h2 id="1-메모리-가시성">1. 메모리 가시성</h2>
<ul>
<li>멀티 스레드 환경에서 한 스레드가 변경한 값이 다른 스레드에 언제 보이는지에 대한 문제를 메모리 가시성이라 함</li>
</ul>
<h3 id="volatile">volatile</h3>
<ul>
<li><p>캐시 메모리를 사용하면 CPU 처리 성능을 개선할 수 있지만 때로는 성능 향상보다 <strong>여러 스레드에서 같은 시점에 정확히 같은 데이터를 보는 것</strong>이 더 중요할 수 있음</p>
</li>
<li><p>해결방안은 성능을 약간 포기하는 대신 값을 읽고 쓸 때 모두 캐시 메모리가 아닌 메인 메모리에 직접 접근하면 됨</p>
</li>
<li><p>volatile 키워드가 해당 기능을 제공</p>
<pre><code class="language-java">public class volatileFlagMain {
  public static void main(String[] args) {
     MyTask task = new MyTask();
      Thread t = new Thread(task, &quot;work&quot;);
      log(&quot;runFlag = &quot; + task.runFlag);
      t.start();

      sleep(1000);
      log(&quot;runFlag를 false로 변경 시도&quot;);
      task.runFlag = false;
      log(&quot;runFlag = &quot; + task.runFlag);
      log(&quot;main 종료&quot;);
  }

  static class MyTask implements Runnable {

      volatile boolean runFlag = true;

      @Override
      public void run() {
          log(&quot;task 시작&quot;);
          while (runFlag) {
              // runFlag false로 변하면 탈출
          }
          log(&quot;task 종료&quot;);
      }
  }
}</code></pre>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/ilov-/post/4a5b4507-3b81-4146-b025-19c382b374cc/image.png" alt=""></p>
<ul>
<li>위 예시에서 volatile 키워드만 추가하면 runFlag에 대해서는 캐시 메모리를 사용하지 않고, 항상 메인 메모리에 직접 접근</li>
<li>runFlag = false 변경 즉시 “task 종료”가 출력되며 자바 프로그램도 종료됨</li>
</ul>
</br>
</br>


</br>
</br>

<h3 id="reference">Reference</h3>
<blockquote>
<p>자바 고급 1편 - 멀티스레드와 동시성</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JAVA] 고급 1편 (멀티스레드와 동시성) 4 - 스레드 제어 (join, interrupt, yield)]]></title>
            <link>https://velog.io/@ilov-/JAVA-%EA%B3%A0%EA%B8%89-1%ED%8E%B8-%EB%A9%80%ED%8B%B0%EC%8A%A4%EB%A0%88%EB%93%9C%EC%99%80-%EB%8F%99%EC%8B%9C%EC%84%B1-4-%EC%8A%A4%EB%A0%88%EB%93%9C-%EC%A0%9C%EC%96%B4-join-interrupt-yield</link>
            <guid>https://velog.io/@ilov-/JAVA-%EA%B3%A0%EA%B8%89-1%ED%8E%B8-%EB%A9%80%ED%8B%B0%EC%8A%A4%EB%A0%88%EB%93%9C%EC%99%80-%EB%8F%99%EC%8B%9C%EC%84%B1-4-%EC%8A%A4%EB%A0%88%EB%93%9C-%EC%A0%9C%EC%96%B4-join-interrupt-yield</guid>
            <pubDate>Sun, 01 Dec 2024 05:51:13 GMT</pubDate>
            <description><![CDATA[<h2 id="1-join">1. Join</h2>
<ul>
<li>main 스레드에서 2개의 작업을 각각 다른 스레드에 지시하고, 그 결과를 받아서 처리하고 싶을 때 
→ Join 사용</li>
</ul>
<h3 id="특정-스레드를-기다리게-하는-방법">특정 스레드를 기다리게 하는 방법</h3>
<ul>
<li>Thread.sleep() 사용<ul>
<li>대기 시간을 손해보고, thread들의 수행시간이 달라지는 경우에 정확한 타이밍을 맞추기 어려움</li>
</ul>
</li>
<li>스레드의 상태가 TERMINATED가 될 때까지 계속 확인하는 방법<ul>
<li>while (thread.getState() ≠ TERMINATED) { … }</li>
<li>계속되는 반복문은 CPU 연산을 사용</li>
</ul>
</li>
<li><strong>join 사용</strong><ul>
<li>join() : 호출 스레드는 대상 스레드가 완료될 때까지 무한정 대기</li>
<li>join(ms) : 호출 스레드는 특정 시간 만큼만 대기,
호출 스레드는 지정한 시간이 지나면 다시 RUNNABLE 상태가 되면서 다음 코드 수행
 → 기다리다 중간에 나오는 상황인데, 결과가 없다면 추가적인 오류 처리가  필요할 수 있음</li>
</ul>
</li>
</ul>
<p><strong>join() 사용 예시</strong></p>
<pre><code class="language-java">public class JoinMainV3 {
    public static void main(String[] args) throws InterruptedException {
        log(&quot;Start&quot;);
        SumTask task1 = new SumTask(1, 50);
        SumTask task2 = new SumTask(51, 100);
        Thread thread1 = new Thread(task1, &quot;thread-1&quot;);
        Thread thread2 = new Thread(task2, &quot;thread-2&quot;);
        thread1.start();
        thread2.start();

        // 스레드가 종료될 때 까지 대기
        log(&quot;join() - main 스레드가 thread1, thread2 종료까지 대기&quot;);
        thread1.join();
        thread2.join();

        log(&quot;main 스레드 대기 완료&quot;);
        log(&quot;task1.result = &quot; + task1.result);
        log(&quot;task2.result = &quot; + task2.result);

        int sumAll = task1.result + task2.result;
        log(&quot;task1 + task2 = &quot; + sumAll);
        log(&quot;End&quot;);
    }

    static class SumTask implements Runnable { ... }
}</code></pre>
<pre><code>실행결과

16:46:56.803 [ main] task1.result = 1275
16:46:56.803 [ main] task2.result = 3775
16:46:56.804 [ main] task1 + task2 = 5050      ← 정확하게 출력됨</code></pre><ul>
<li>join()을 실행하게되면 main 스레드는 thread-1, thread-2가 종료될 때까지 기다림 (main 스레드는 WAITING  상태)</li>
</ul>
<pre><code class="language-java">   log(&quot;join() 실행&quot;);   // main - RUNNABLE
   thread1.join();      // main - WAITING
                        // main - RUNNABLE
   thread2.join();      // main - WAITING</code></pre>
<ul>
<li>join(ms) 를 사용하게 되면 WAITING이 아니라, TIMED_WAITING이 됨</li>
</ul>
</br>
</br>
</br>

<h2 id="2-interrupt">2. Interrupt</h2>
<ul>
<li><p>특정 스레드의 작업을 중간에 중단하기 위해 사용</p>
<h3 id="i-interrupt">i) interrupt()</h3>
</li>
<li><p>WAITING, TIMED_WAITING 같은 대기 상태의 스레드를 직접 깨워서, 작동하는 RUNNABLE 상태로 만들 수 있음</p>
<blockquote>
<p><strong>InterruptedException</strong></p>
<ul>
<li><code>Thread.sleep()</code>, <code>wait()</code>, 또는 <code>join()</code>와 같은 블로킹 메서드가 실행 중일 때, 스레드가 &gt;<strong>interrupt</strong> 상태로 설정되면 발생</li>
<li>if any thread has interrupted the current thread. The interrupted status of the current thread is cleared when this exception is thrown.</li>
</ul>
</blockquote>
</li>
<li><p>특정 스레드의 인스턴스에 interrupt() 메서드를 호출</p>
<p>  → Thread.sleep() 상태에 있던 스레드에  InterruptedException  발생</p>
<p>  → 이때 인터럽트를 받은 스레드는 대기 상태에서 깨어나 RUNNABLE 상태가 되고 코드 정상 수행</p>
<p>  → InterruptedException을 catch로 잡아서 정상 흐름으로 변경</p>
</li>
</ul>
</br>

<h3 id="ii-isinterrupted---인터럽트-상태-확인-상태-변경-x">ii) isInterrupted() - 인터럽트 상태 확인 (상태 변경 X)</h3>
<ul>
<li>위 예제에서 sleep()과 같은 메서드를 호출하고 나서야 인터럽트가 발생하는 문제를 해결해보자</li>
<li>sleep() 없이도 while 조건문에서 isInterrupted()를 통해 interrupt() 호출 즉시 인터럽트 상태를 확인하여 반복문 탈출 가능</li>
<li>하지만 계속 interrupt 상태를 유지하여 Thread.sleep()과 같은 코드에서 InterruptedException 발생시킴
  → 즉, 계속해서 interrupt 상태를 유지 하고 있단 뜻
  → 우리가 원하는 것은 while()문을 탈출하기 위해 딱 한번만 인터럽트를 사용하는 것이지 계속 인터럽트 상태를 유지하기를 원하는 것이 아님
  → 그래서 자바에서는 인터럽트 예외가 발생하면 스레드의 인터럽트 상태를 다시 정상(false)으로 돌림</li>
</ul>
</br>

<h3 id="iii-threadinterrupted----인터럽트-상태-확인상태-변경-o">iii) Thread.interrupted()  - 인터럽트 상태 확인(상태 변경 O)</h3>
<ul>
<li>직접 체크해서 사용할 때는 Thread.interrupted()를 사용해야 함<ul>
<li>스레드가 인터럽트 상태라면 <code>true</code> 를 반환하고, 해당 스레드의 인터럽트 상태를 <code>false</code>로 변경</li>
<li>스레드가 인터럽트 상태가 아니라면 <code>false</code> 를 반환하고, 해당 스레드의 인터럽트 상태를 변경하지 않는다.</li>
</ul>
</li>
</ul>
<p></br></br>   </p>
<h2 id="3-yield">3. Yield</h2>
<ul>
<li>어떤 스레드를 얼마나 실행할지는 운영체제가 스케줄링을 통해 결정하지만 양보하고 싶을 수 있음</li>
<li>양보하면 스케줄링 큐에 대기 중인 다른 스레드가 CPU 실행 기회를 더 빨리 얻을 수 있음</li>
</ul>
<pre><code class="language-java">    public class YieldClass {
    static final int THREAD_COUNT = 1000;

    public static void main(String[] args) {
        for (int i = 0; i &lt; THREAD_COUNT; i++) {
            Thread thread = new Thread(new MyRunnable());
            thread.start();
        }
    }

    static class MyRunnable implements Runnable {

        @Override
        public void run() {
            for (int i = 0; i &lt; 10; i++) {
                System.out.println(Thread.currentThread().getName() + &quot; - &quot; + i);

                                   // 1. empty
                // sleep(1);       // 2. sleep
                // Thread.yield()  // 3. yield
            }
        }
    }
}</code></pre>
<ul>
<li><ol>
<li><code>Empty</code> : <code>sleep(1)</code> , <code>yield()</code> 없이 호출한다. 운영체제의 스레드 스케줄링을 따른다.</li>
</ol>
</li>
<li><ol start="2">
<li><code>sleep(1)</code> : 특정 스레드를 잠시 쉬게 한다.<ul>
<li>아주 잠깐 RUNNABLE → TIMED_WAITING 으로 변경되며 스케줄링에서 제외</li>
<li>어떠한 스레드도 실행되지 않을 수 있음</li>
</ul>
</li>
</ol>
</li>
<li><ol start="3">
<li><code>yield()</code> : <code>yield()</code> 를 사용해서 다른 스레드에 실행을 양보한다.<ul>
<li>양보할 스레드가 없다면 본인 스레드가 계속 실행될 수 있음</li>
<li>현재 실행중인 스레드가 자발적으로 CPU를 양보하여 다른 스레드가 실행될 수 있도록 함</li>
<li>yield()를 호출한 스레드는 RUNNABLE 상태를 유지하면서 CPU를 양보 (스케쥴링 큐에 들어가면서 양보)</li>
</ul>
</li>
</ol>
</li>
</ul>
<p></br></br></br></br></br></p>
<h3 id="reference">Reference</h3>
<blockquote>
<p><a href="https://www.inflearn.com/course/%EA%B9%80%EC%98%81%ED%95%9C%EC%9D%98-%EC%8B%A4%EC%A0%84-%EC%9E%90%EB%B0%94-%EA%B3%A0%EA%B8%89-1">자바 고급 1편 - 멀티스레드와 동시성</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JAVA] 고급 1편 (멀티스레드와 동시성) 3 -  스레드 생명주기와 체크 예외 재정의]]></title>
            <link>https://velog.io/@ilov-/JAVA-%EA%B3%A0%EA%B8%89-1%ED%8E%B8-%EB%A9%80%ED%8B%B0%EC%8A%A4%EB%A0%88%EB%93%9C%EC%99%80-%EB%8F%99%EC%8B%9C%EC%84%B1-3-%EC%8A%A4%EB%A0%88%EB%93%9C-%EC%83%9D%EB%AA%85%EC%A3%BC%EA%B8%B0%EC%99%80-%EC%B2%B4%ED%81%AC-%EC%98%88%EC%99%B8-%EC%9E%AC%EC%A0%95%EC%9D%98</link>
            <guid>https://velog.io/@ilov-/JAVA-%EA%B3%A0%EA%B8%89-1%ED%8E%B8-%EB%A9%80%ED%8B%B0%EC%8A%A4%EB%A0%88%EB%93%9C%EC%99%80-%EB%8F%99%EC%8B%9C%EC%84%B1-3-%EC%8A%A4%EB%A0%88%EB%93%9C-%EC%83%9D%EB%AA%85%EC%A3%BC%EA%B8%B0%EC%99%80-%EC%B2%B4%ED%81%AC-%EC%98%88%EC%99%B8-%EC%9E%AC%EC%A0%95%EC%9D%98</guid>
            <pubDate>Sun, 01 Dec 2024 05:33:18 GMT</pubDate>
            <description><![CDATA[<h2 id="1-스레드의-생명주기">1. 스레드의 생명주기</h2>
<p><img src="https://velog.velcdn.com/images/ilov-/post/a2276f13-c307-4c36-970e-c2029326e3f0/image.png" alt=""></p>
<h3 id="스레드의-상태">스레드의 상태</h3>
<pre><code class="language-java">public class Thread implements Runnable {
    ...
    public enum State {
        NEW,             // 스레드가 아직 시작되지 않은 상태
        RUNNABLE,        // 스레드가 실행중이거나 실행 준비가 된 상태
        BLOCKED,         // 스레드가 동기화 락을 기다리는 상태
        WAITING,         // 스레드가 다른 스레드의 특정 작업이 완료되기를 기다리는 상태
        TIMED_WAITING,   // 일정 시간 동안 기다리는 상태
        TERMINATED;      // 스레드가 실행을 마친 상태
    }
}</code></pre>
<ul>
<li><p><em>RUNNABLE (실행상태)</em></p>
<ul>
<li>start() 메서드가 호출되면 스레드는 이 상태로 들어감</li>
<li>CPU에서 실행될 수 있는 상태, 
운영체제 스케줄러의  실행 대기열에 있든, CPU에서 실제 실행되고 있든 모두 RUNNABLE 상태</li>
</ul>
</li>
<li><p><em>BLOCKED (차단상태)</em></p>
<ul>
<li>다른 스레드에 의해 동기화  락을 얻기위해  기다리는 상태<ul>
<li>예) synchronized  블록에 진입하기 위해 락을 얻어야 하는 경우<pre><code>`synchronized (lock) { … }`  코드 블록에 진입하려고 할 때, 다른 스레드가 이미 lock을 가지고 있는 경우</code></pre></li>
</ul>
</li>
</ul>
</li>
<li><p><em>WAITING (대기상태)</em></p>
<ul>
<li><p>wait(), join() 호출 시의 상태</p>
</li>
<li><p>다른 스레드가 notify(), notifyAll() 메서드를 호출하거나, join()이 완료될 때까지 기다림</p>
</li>
<li><p>join()을 호출하는 스레드는 대상 스레드가 TERMINATED 상태가 될 떄까지 대기</p>
<p>  대상 스레드가  TERMINATED 상태가 되면 호출 스레드는 다시 RUNNABLE 상태가 되면서 다음 코드를 수행</p>
</li>
</ul>
</li>
<li><p><em>TIMED_WAITING (시간제한 대기 상태)</em></p>
<ul>
<li><code>sleep(long millis)</code> , <code>wait(long timeout)</code> , <code>join(long millis)</code> 메서드가 호출될 때의 상태</li>
<li>주어진 시간이 경과하거나 다른 스레드가 해당 스레드를 깨우면 이 상태에서 벗어남
예: <code>Thread.sleep(1000);</code></li>
</ul>
</li>
<li><p>TERMINATED (종료 상태)</p>
<ul>
<li>스레드가 정상적으로 종료되거나 예외가 발생하여 종료된 경우의 상태</li>
<li>스레드는 한 번 종료되면 다시 시작할 수 없음</li>
</ul>
</li>
</ul>
</br>
</br>


<p><strong>자바 스레드의 상태 전이 과정</strong></p>
<ol>
<li><p><em>[ New → Runnable ]</em> : <code>start()</code> 메서드를 호출하면 스레드가 <code>Runnable</code> 상태로 전이된다.</p>
</li>
<li><p><em>[ Runnable → Blocked/Waiting/Timed Waiting ]</em> : 스레드가 락을 얻지 못하거나, <code>wait()</code> 또는 <code>sleep()</code> 메서드를 호출할 때 해당 상태로 전이된다.</p>
</li>
<li><p><em>[ Blocked/Waiting/Timed Waiting → Runnable ]</em> : 스레드가 락을 얻거나, 기다림이 완료되면 다시 <code>Runnable</code> 상태로 돌아감</p>
</li>
<li><p><em>[ Runnable → Terminated ]</em> : 스레드의 <code>run()</code> 메서드가 완료되면 스레드는 <code>Terminated</code> 상태가 된다.</p>
</li>
</ol>
</br>
</br>

<h2 id="2-체크-예외-재정의">2. 체크 예외 재정의</h2>
<ul>
<li>Runnable 인터페이스의 run() 메서드를 구현할 때 InterruptedException 체크 예외를 밖으로 던질 수 없음</li>
<li>Runnable 인터페이스</li>
</ul>
<pre><code class="language-java">    public interface Runnable {
            void run();
    }</code></pre>
<ul>
<li><p>자바에서 메서드를 재정의할 때, 재정의 메서드가 지켜야할 예외와 관련된 규칙</p>
<ul>
<li><p>체크 예외</p>
<ul>
<li>부모 메서드가 체크 예외를 던지지 않는 경우, 재정의된 자식 메서드도 체크 예외를 던질 수 없다.</li>
<li>자식 메서드는 부모 메서드가 던질 수 있는 체크 예외의 하위 타입만 던질 수 있다.</li>
</ul>
</li>
<li><p>언체크 예외</p>
<ul>
<li>예외 처리를 강제하지 않으므로 상관없이 던질 수 있음</li>
</ul>
<p>→ Runnable 인터페이스의 run() 메서드는 아무런 체크 예외를 던지지 않으므로 run() 메서드를 재정의 하는 곳에서는 체크 예외를 밖으로 던질 수 없음</p>
</li>
</ul>
</li>
</ul>
</br>
</br>

<h3 id="reference">Reference</h3>
<blockquote>
<p>자바 고급 1편 - 멀티스레드와 동시성</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JAVA] 고급 1편 (멀티스레드와 동시성) 2 - 스레드 생성과 실행]]></title>
            <link>https://velog.io/@ilov-/%EC%9E%90%EB%B0%94-%EA%B3%A0%EA%B8%89-1%ED%8E%B8-%EB%A9%80%ED%8B%B0%EC%8A%A4%EB%A0%88%EB%93%9C%EC%99%80-%EB%8F%99%EC%8B%9C%EC%84%B1-2</link>
            <guid>https://velog.io/@ilov-/%EC%9E%90%EB%B0%94-%EA%B3%A0%EA%B8%89-1%ED%8E%B8-%EB%A9%80%ED%8B%B0%EC%8A%A4%EB%A0%88%EB%93%9C%EC%99%80-%EB%8F%99%EC%8B%9C%EC%84%B1-2</guid>
            <pubDate>Sat, 16 Nov 2024 09:19:59 GMT</pubDate>
            <description><![CDATA[<h2 id="1-스레드-생성">1 .스레드 생성</h2>
<ul>
<li>스레드를 생성하는 방법에는 Thread를 상속받는 방법과 Runnable 인터페이스를 구현하는 방법이 있음<pre><code class="language-java"></code></pre>
</li>
</ul>
<p>// Thread 상속
public class HelloThread extends Thread {</p>
<pre><code>@Override
public void run() {
    System.out.println(Thread.currentThread().getName() +  &quot;: run()&quot;);
}</code></pre><p>}</p>
<p>// Runnable 인터페이스 구현
public class HelloRunnable implements Runnable {</p>
<pre><code>@Override
public void run() {
    System.out.println(Thread.currentThread().getName() + &quot;: run()&quot;);
}</code></pre><p>}</p>
<p>// start()로 실행
public class HelloRunnableMain {
    public static void main(String[] args) {
        System.out.println(Thread.currentThread().getName() + &quot;: main() start&quot;);</p>
<pre><code>    HelloRunnable runnable = new HelloRunnable();
    Thread thread = new Thread(runnable);
    thread.start();

    System.out.println(Thread.currentThread().getName() + &quot;: main() end&quot;);
}</code></pre><p>}</p>
<pre><code>
- 스레드 생성 전
 ![](https://velog.velcdn.com/images/ilov-/post/ee575e74-7534-4e38-9069-68bf4f8e34cc/image.png)


    - 자바는 실행 시점에 main이라는 이름의 스레드를 만들고 프로그램의 시작점인 main() 메서드 실행
- 스레드 생성 후
![](https://velog.velcdn.com/images/ilov-/post/262e0aa4-b5d1-4e4d-8882-fc99395227b3/image.png)

   - 스레드 객체를 생성하고 반드시 start()를 호출해야 스택 공간을 할당받고 스레드가 작동함
   - 스레드에 이름을 주지않으면 Thread-0, Thread-1과 같은 임의의 이름을 부여함
   - Thread-0 스레드는 run() 메서드의 스택 프레임을 스택에 올리면서 run() 메서드를 시작함
   - 스레드는 순서와 실행 기간을 보장하지 않음 →  멀티 스레드임을 알 수 있음


&lt;/br&gt;

### Thread 상속 vs Runnable 구현

- **Thread** **클래스** **상속** **방식**
    - 장점
        - 간단한 구현: `Thread` 클래스를 상속받아 `run()` 메서드만 재정의하면 된다.
    - 단점
        - 상속의 제한: 자바는 단일 상속만을 허용하므로 이미 다른 클래스를 상속받고 있는 경우 `Thread` 클래스를 상속받을 수 없다.
        - 유연성 부족: 인터페이스를 사용하는 방법에 비해 유연성이 떨어진다.
- **Runnable** **인터페이스를** **구현** **방식**
    - 장점
        - 상속의 자유로움: `Runnable` 인터페이스 방식은 다른 클래스를 상속받아도 문제없이 구현할 수 있다.
        - 코드의 분리: 스레드와 실행할 작업을 분리하여 코드의 가독성을 높일 수 있다.
        - 여러 스레드가 동일한 `Runnable` 객체를 공유할 수 있어 자원 관리를 효율적으로 할 수 있다.
    - 단점
        - 코드가 약간 복잡해질 수 있다. `Runnable` 객체를 생성하고 이를 `Thread` 에 전달하는 과정이 추가된다.

&lt;/br&gt;
&lt;/br&gt;
&lt;/br&gt;
&lt;/br&gt;

## 2. 데몬 스레드

- 스레드는 사용자 스레드와 데몬 스레드 2가지 종류로 구분 가능
- 사용자 스레드 (User Thread)
    - 프로그래의 주요 작업을 수행함
    - 작업이 완료될 때까지 실행됨
    - 모든 user스레드가 종료되면 JVM도 종료됨
- 데몬 스레드 (daemon Thread)
    - 백그라운드에서 보조적인 작업 수행
    - 모든 user스레드가 종료되면 데몬 스레드는 자동으로 종료됨
    - JVM은 데몬 스레드의 실행 완료를 기다리지 않고 종료됨
    - 접적으로 보이지 않으면서 시스템의 백그라운드에서 작업을 수행하는 것을 데몬 스레드, 데몬 프로세스라 함

&lt;/br&gt;&lt;/br&gt;&lt;/br&gt;&lt;/br&gt;   
## 3 .Runnable을 만드는 다양한 방법
i) 정적 중첩 클래스 사용

- 특정 클래스 안에서만 사용되는 경우 중첩 클래스 사용

```java
    public class InnerRunnableMainV1 {
        public static void main(String[] args) {
            log(&quot;main() start&quot;);

            Runnable runnable = new MyRunnable();
            Thread thread = new Thread(runnable);
            thread.start();

            log(&quot;main() end&quot;);
        }

        static class **MyRunnable** implements Runnable {

            @Override
            public void run() {
                log(&quot;run()&quot;);
            }
        }
    }</code></pre><p>ii) 익명 클래스 사용</p>
<ul>
<li>특정 메서드 안에서만 간단히 정의하고 사용하고 싶다면 익명 클래스 사용하면 됨</li>
</ul>
<p>iii) 람다 사용</p>
<ul>
<li><p>람다를 사용하면 메서드 코드 조각 전달 가능</p>
<pre><code class="language-java">public class InnerRunnableMainV4 {
  public static void main(String[] args) {
      log(&quot;main() start&quot;);

      Thread thread = new Thread(() -&gt; log(&quot;run()&quot;));
      thread.start();

      log(&quot;main() end&quot;);
  }
}
</code></pre>
</li>
</ul>
<pre><code>
&lt;/br&gt;&lt;/br&gt;&lt;/br&gt;&lt;/br&gt;&lt;/br&gt;&lt;/br&gt;&lt;/br&gt;&lt;/br&gt;

### Reference
&gt; [자바 고급 1편 - 멀티스레드와 동시성](https://www.inflearn.com/course/%EA%B9%80%EC%98%81%ED%95%9C%EC%9D%98-%EC%8B%A4%EC%A0%84-%EC%9E%90%EB%B0%94-%EA%B3%A0%EA%B8%89-1)</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[JAVA] 고급 1편 (멀티스레드와 동시성) 1 - 프로세스와 스레드]]></title>
            <link>https://velog.io/@ilov-/%EC%9E%90%EB%B0%94-%EA%B3%A0%EA%B8%89-1%ED%8E%B8-%EB%A9%80%ED%8B%B0%EC%8A%A4%EB%A0%88%EB%93%9C%EC%99%80-%EB%8F%99%EC%8B%9C%EC%84%B1-1</link>
            <guid>https://velog.io/@ilov-/%EC%9E%90%EB%B0%94-%EA%B3%A0%EA%B8%89-1%ED%8E%B8-%EB%A9%80%ED%8B%B0%EC%8A%A4%EB%A0%88%EB%93%9C%EC%99%80-%EB%8F%99%EC%8B%9C%EC%84%B1-1</guid>
            <pubDate>Sat, 09 Nov 2024 14:12:36 GMT</pubDate>
            <description><![CDATA[<h1 id="프로세스와-스레드">프로세스와 스레드</h1>
<ul>
<li><p>컴퓨터의 구조
<img src="https://velog.velcdn.com/images/ilov-/post/9f0f90b0-4d36-4f32-bdea-ed1033480b83/image.png" alt=""></p>
<h3 id="1-프로세스">1. 프로세스</h3>
</li>
<li><p>운영체제 안에서 실행 중인 프로그램, 프로세스는 프로그램의 인스턴스</p>
</li>
<li><p>실행되기 전 <strong>프로그램은</strong> 보조기억장치에 저장된 데이터였다가 운영체제가 메모리에 적재하고 실행되는 순간 프로세스가 만들어짐</p>
</li>
<li><p>프로그램 실행 = 프로세스 안의 코드가 한 줄씩 실행되는 것</p>
</li>
<li><p>종류 : foreground process(사용자가 볼 수 있는 프로세스), background process(유닉스 - 데몬, 윈도우 - 서비스)</p>
</li>
<li><p>역할</p>
<ul>
<li>실행 환경을 제공(메모리 공간, 파일 핸들, 시스템 자원(네트워크 연결)) - 컨테이너 역할</li>
<li>프로세스 자체는 운영체제의 스케줄러에 의해 직접 실행되지 않으며, 프로세스 내의 스레드가 실행됨</li>
</ul>
</li>
</ul>
<p><strong>프로세스 제어 블록(Process Control Block)</strong></p>
<ul>
<li>운영체제가 프로세스의 실행 순서를 관리하고, 프로세스에 CPU를 비롯한 자원을 배분하기 위해 사용하는 것</li>
<li>프로세스와 관련된 정보를 저장하는 자료 구조(커널 영역에 생성 됨)</li>
<li>프로세스 생성 시에 만들어지고 실행이 끝나면 폐기됨</li>
<li>담기는 정보<ul>
<li>프로세스 ID(PID), 레지스터 값, 프로세스 상태, CPU 스케줄링 정보, 메모리 관리 정보, 사용한 파일과 입출력장치 목록</li>
</ul>
</li>
</ul>
<p><strong>문맥 교환(Context Swithcing)</strong></p>
<ul>
<li>프로세스의 문맥을 PCB에 백업하고 새로운 프로세스를 실행하기 위해 문맥을 PCB로부터 복구하여 새로운 프로세스를 실행하는 것</li>
<li>문맥 : 하나의 프로세스 수행을 재개하기 위해 기억해야 할 정보 (PCB에 기록될 정보)</li>
<li>하나의 프로세스 문맥은 해당 프로세스의 PCB에 표현되어 있음</li>
</ul>
<p><strong>프로세스의 메모리 영역</strong></p>
<ul>
<li>프로세스가 생성되면 <strong>커널 영역 - PCB</strong>, <strong>사용자 영역 - 프로세스</strong>가 저장됨<ul>
<li>사용자 영역에 코드, 데이터, 힙, 스택 영역으로 나뉘어 저장됨</li>
</ul>
</li>
<li>정적 할당 영역<ul>
<li><strong>코드 영역(Code Segment)</strong> - 텍스트 영역<ul>
<li>실행할 수 있는 코드, 기계어로 이루어진 명령어가 저장됨</li>
<li>데이터가 아닌 CPU가 실행할 명령어가 담겨있기 때문에 쓰기가 금지되어 있음(READ_ONLY 공간)</li>
</ul>
</li>
<li><strong>데이터 영역(Data Segment)</strong><ul>
<li>프로그램이 실행되는 동안 유지할 데이터가 저장되는 공간 (전역 변수같은 데이터)</li>
</ul>
</li>
</ul>
</li>
<li>동적 할당 영역<ul>
<li><strong>힙 영역(Heap Segment)</strong><ul>
<li>프로그래머가 직접 할당할 수 있는 저장 공간, 할당하고 사용 후 반환해야 함</li>
</ul>
</li>
<li><strong>스택 영역(Stack Segmnet)</strong><ul>
<li>메서드 호출 시 생성되는 지역 변수와 반환 주소가 저장되는 영역(스레드에 포함)</li>
<li>데이터를 일시적으로 저장하는 공간(매개 변수, 지역 변수 등)</li>
<li>일시적으로 저장할 데이터는 스택 영역에 push되고, 더 이상 필요하지 않을 때 pop되어 사라짐
<img src="https://velog.velcdn.com/images/ilov-/post/53646502-18f4-4529-8bd0-f7f242548f3b/image.png" alt=""></li>
</ul>
</li>
</ul>
</li>
</ul>
</br>



<h3 id="2-스레드">2. 스레드</h3>
<ul>
<li>프로세스 : 실행되는 프로그램</li>
<li>스레드 : 프로세스를 구성하는 실행의 흐름 단위</li>
<li>하나의 프로세스는 여러 스레드를 가질 수 있음</li>
<li>스레드를 이용해 하나의 프로세스에서 여러 부분을 동시에 실행 가능</li>
</ul>
<p><strong>스레드의 구성 요소</strong></p>
<ul>
<li>스레드 ID, 프로그램 카운터 값을 비롯한 레지스터 값, 스택
→ 그래서 스레드마다 각기 다른 코드 실행 가능
→ 프로세스의 스레드들은 실행에 필요한 레지스터 값, 스택만을 유지한 채 <strong>프로세스 자원을 공유하며 실행</strong>됨</li>
</ul>
<p><strong>스레드의 메모리 구성</strong>    </p>
<ul>
<li>공유 메모리 : 같은 프로세스의 코드 섹션, 데이터 섹션, 힙(메모리)은 프로세스 안의 모든 스레드가 공유</li>
<li>개별 스택    : 각 스레드는 자신의 스택을 가지고 있음</li>
</ul>
</br>


<h3 id="3-멀티태스킹-멀티-프로세스-멀티-스레드">3. 멀티태스킹, 멀티 프로세스, 멀티 스레드</h3>
<ul>
<li>프로세스는 실행 환경과 자원을 제공하는 컨테이너 역할을 하고, 스레드는 CPU를 사용해서 코드 하나하나를 실행</li>
<li>멀티 태스킹 (운영체제 소프트웨어적 관점)<ul>
<li>단일 CPU(단일 CPU core)가 여러 작업을 동시에 수행하는 것처럼 보이게 하는 것을 의미</li>
<li>소프트웨어 기반으로 CPU  시간을 분할하여 각 작업에 할당</li>
<li>예) 현대 운영체제에서 여러 어플리케이션이 동시에 실행되는 환경</li>
</ul>
</li>
<li>멀티 프로레싱 (하트웨어 장비 관점)<ul>
<li>여러 CPU(여러 CPU core)를 사용하여 동시에 여러 작업을 수행하는 것을  의미</li>
<li>하드웨어 기반으로 성능 향상시킴</li>
<li>예) 다중  코어 프로세서를 사용하는 현대 컴퓨터 시스템</li>
</ul>
</li>
</ul>
<p>→ 여러 CPU 코어를 사용하며 (멀티 프로세싱), 동시에 각각의 단일 CPU 코어에 여러 작업을 분할해서 수행(멀티 태스킹) 가능</p>
</br>
</br>
</br>
</br>
</br>
</br>
</br>
</br>
</br>
</br>


<hr>
<h3 id="reference">Reference</h3>
<blockquote>
<p><a href="https://www.inflearn.com/course/%EA%B9%80%EC%98%81%ED%95%9C%EC%9D%98-%EC%8B%A4%EC%A0%84-%EC%9E%90%EB%B0%94-%EA%B3%A0%EA%B8%89-1">자바 고급 1편 - 멀티스레드와 동시성</a>
개발 서적 - 혼자 공부하는 컴퓨터 구조 &amp; 네트워크 (강민철 지음)</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[Kubernetes]]></title>
            <link>https://velog.io/@ilov-/k8s</link>
            <guid>https://velog.io/@ilov-/k8s</guid>
            <pubDate>Mon, 30 Sep 2024 03:29:39 GMT</pubDate>
            <description><![CDATA[<h1 id="kubernetes">Kubernetes</h1>
<p><strong>용어</strong></p>
<blockquote>
<ul>
<li>manifest : deployment.yaml, service.yaml 등의 파일들의 통칭</li>
</ul>
</blockquote>
<ul>
<li>helm : deployment.yaml, service.yaml 등을 하나로 묶어 관리하는 <strong>&#39;패키지 매니저&#39;</strong></li>
<li>kubectl : K8s 관리소에 <strong>명령(Manifest 전달)</strong>을 내리는 핵심 도구<ul>
<li>Master에서만 사용 가능하고, k8s에서 관리하는 자원을 생성, 가져오기 및 삭제하는 작업 수행</li>
</ul>
</li>
</ul>
<blockquote>
<ul>
<li><strong>Cluster</strong> : 가장 기본적인 단위, 여러 개의 node로 구성되며 마스터 노드(Control Plane)와 워커 노드(Worker Node)로 나뉨</li>
</ul>
</blockquote>
<ul>
<li><strong>Namespaces</strong> : 동일한 물리 클러스터를 기반으로 하는 여러 가상 클러스터를 지원하며, 이런 가상 클러스터를 말함. 논리적인 분리 단위이며, Namespace 별로 클러스터 내에서 논리적으로 리소스를 분리하게 함
-&gt; 리소스 중 object는 namespaces, service, volume, pod </li>
<li><strong>Controller</strong> : k8s는 다양한 컨트롤러를 통해 애플리케이션을 자동으로 배포/관리하고 문제 발생 시 복구할 수 있도록 지원</li>
<li><strong>Ingress</strong> : 클러스터 외부에서 내부 서비스(Pod)로 접근할 수 있도록 트래픽을 제어하는 역할</li>
<li><strong>Node</strong> : k8s 클러스터를 구성하는 실제 컴퓨터(ec2 등)를 의미</li>
<li><strong>Pod</strong> : k8s에서 생성하고 관리할 수 있는 배포 가능한 가장 작은 컴퓨팅 단위 (컨테이너의 그룹)
-&gt; 스토리지 및 네트워크를 공유하고, 해당 컨테이너를 구동하는 방식에 대한 명세 또는 정의를 갖게 됨</li>
</ul>
</br>
</br>

<h2 id="cluster">Cluster</h2>
<p><img src="https://velog.velcdn.com/images/ilov-/post/1ca1e1ad-a5d7-4cf5-a2d6-13584ee0d3f1/image.png" alt=""></p>
<ul>
<li><p><strong>cluster</strong> = <strong>master</strong> 노드 + <strong>worker</strong> 노드</p>
</li>
<li><p><strong>master 노드</strong>(control plane)</p>
<ul>
<li>k8s 클러스터의 모든 상태를 저장 및 관리</li>
<li>직접 앱을 올리는 곳이 아니라 지시를 내리는 곳</li>
<li>component : API server, Etcd, Scheduler, Controller Manager</li>
<li><strong>API server</strong> : 클러스터의 모든 요청과 응답을 처리하는 중앙통로, REST API를 제공하며 kubectl 명령이나 worker 노드의 보고를 가장 먼저 받음</li>
<li><strong>Etcd</strong>(상태 저장소) : 클러스터의 모든 상태 정보가 저장되는 중요한 key-value 데이터베이스(pod가 몇 개인지 등)</li>
<li><strong>Scheduler</strong>(배치 관리자) : 새로 생성된 Pod를 어떤 Worker Node에 배치할지 결정합니다.</li>
<li><strong>Controller-Manager</strong>(상태 유지) : 현재 상태와 원하는 상태를 끊임없이 비교하며 상태를 일치시킵니다. (예: 파드가 죽으면 새로 생성)</li>
</ul>
</li>
<li><p><strong>worker 노드</strong></p>
<ul>
<li>마스터 노드의 지시를 받아 컨테이너(Pod)를 실제로 실행하고 유지</li>
<li>component : kublet, pod</li>
<li><strong>kublet</strong> : Kubernetes 노드에서 실행되는 에이전트로, 클러스터의 마스터와 노드 간의 중요한 다리 역할, 지시를 받아 컨테이너 런타임에 전달</li>
<li><strong>kube-porxy</strong> : 노드 수준애서 Service 네트워크 규칙을 관리하고 트래픽을 전달</li>
<li><strong>Container Runtime</strong>(엔진) : Docker나 Containerd 같이 실제 컨테이너를 실행하는 소프트웨어</li>
</ul>
</li>
</ul>
<blockquote>
<ul>
<li>kubectl ➔ Master(API server) ➔ Worker(kubelet) ➔ Worker(docker)가 Container 생성 </li>
</ul>
</blockquote>
<ul>
<li>Deployment라는 컨트롤러가 ReplicaSet을 만들고, 이 ReplicaSet이 Pod의 개수를 유지</li>
<li>Service가 가진 고정 IP를 호출하면, Service가 알아서 현재 살아있는 Pod들 중 하나로 트래픽을 전달해줌<blockquote>
</blockquote>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/ilov-/post/d372134d-9dd3-47fc-80b9-d75e8047fe61/image.png" alt="">
<img src="https://velog.velcdn.com/images/ilov-/post/df3f65bf-6f57-4c74-a9b2-f9d40157df7a/image.png" alt=""></p>
<ul>
<li>같은 내용을 담은 이미지</li>
<li>namespace로 논리적으로 리소스를 구분하고 master, worker로 이루어져있으며 각각의 노드에는 필요한 component들이 있고 Api server, kublet로 통신</li>
<li>개발자가 kubectl로 명령을 내림 -&gt; 마스터의 API Server가 접수 -&gt; 오른쪽 master의 네트워크망을 타고 각 Subnet에 있는 EC2의 Kubelet에게 지시를 내려 Pod를 띄우게 됩니다.</li>
</ul>
</br>
</br>

<h2 id="controller">Controller</h2>
<p><img src="blob:https://velog.io/cb980e31-a2ec-4036-a2ff-f6f199ba52c2" alt="업로드중.."></p>
<ul>
<li>k8s는 다양한 컨트롤러를 통해 애플리케이션을 자동으로 배포/관리하고 문제 발생 시 복구할 수 있도록 지원</li>
</ul>
<p><strong>Deployment</strong> : 지정된 수의 Pod를 항상 유지하며, 애플리케이션을 점진적으로 업데이트하거나 문제가 생겼을 때 이전 버전으로 롤백할 수 있도록 도움
<strong>StatefulSet</strong> : 각 Pod에 고유한 네트워크 ID와 영구 저장소가 필요한 상태 기반 애플리케이션을 안정적으로 운영할 수 있게 함
<strong>DaemonSet</strong>
<strong>Job &amp; CronJob</strong></p>
<h2 id="service">Service</h2>
<ul>
<li><p>동일한 서비스를 제공하는 여러 개의 Pod에 접근할 수 있는 하나의 IP를 제공하는 네트워크 노출을 위한 특정 리소스(객체) (Pod에 연결시켜주는 Network)</p>
<ul>
<li>Service의 IP로 접속할 때 Pod으로 연결시켜주는 원리는 Service는 자신이 가지고 있는 Selector 값과 Cluster에서 운영중인 Pod의 Label 값이 동일한 Pod으로 연결시켜줌</li>
</ul>
</li>
<li><p>Controller를 사용하여 Pod를 실행하고 관리하지만, 실행된 Pod에 접근할 때는 Service를 이용</p>
</li>
<li><p>Pod는 기본적으로 한 Node에서만 실행되지 않고 상황에 따라 Cluster 내에 속한 여러 Node로 옮겨 다님
-&gt; 그러므로 Pod의 IP로 접속하는데는 한계가 있고 동일한 서비스의 대표 IP인 Service의 IP로 접근하는 것이 편리함</p>
</li>
<li><p>종류 : ClusterIP, NodePort, LoadBalancer, ExternalName</p>
</li>
</ul>
<p><strong>ClusterIP</strong></p>
<ul>
<li>Default Service로서 Pod 그룹의 단일 진입점(Virtual IP) 생성</li>
</ul>
<p><strong>NodePort</strong></p>
<ul>
<li>ClusterIP가 동시에 만들어진다</li>
<li>Cluster내의 아무 Worker Node의 IP에 대한 Port 번호로 접속하도록 함(기본적으로 30,000 ~ 32,767까지 포트 번호가 동적으로 할당됨)</li>
</ul>
<p><strong>LoadBalancer</strong></p>
<ul>
<li>Layer 2 ~ Layer 4를 지원하는 부하조정 장비</li>
<li>Public Cloud 업체 및 OpenStack에서 이용 가능(OnPremises Cluster에서는 metallb 사용 가능)</li>
<li>load Balancer를 만들면 ClusterIP와 NodePort가 동시에 만들어짐
(외부에서 접속 시 Load Balancer ➔ NodePort ➔ ClusterIP로 거쳐서 결국 Pod에 접속하게 됨)</li>
</ul>
<p><strong>ExternalName</strong></p>
<ul>
<li>Cluster 안에서 외부에 접속시 사용할 도메인을 등록해서 사용</li>
<li>Cluster domain이 실제 외부 도메인으로 치환되어 동작</li>
</ul>
<p><img src="https://velog.velcdn.com/images/ilov-/post/ed6c1b54-2f2a-4bb6-8934-1de1869db4f2/image.png" alt=""></p>
<blockquote>
<p>pod 접속</p>
</blockquote>
<ul>
<li>클러스터 내에서 Pod 접속 → Cluster IP</li>
<li>클러스터 밖에서 Pod 접속 → Node Port</li>
<li>클러스터 밖에서 Pod 접속 → Load Balancer
-&gt; 최종적으로 밖에서 Pod에 접속 할 때 <strong>Load Balancer → Node Port→ Cluster IP</strong>로 접속</li>
</ul>
<p><strong>정리</strong></p>
<blockquote>
<p><strong>1. 컨트롤러(Controller)를 통한 Pod 관리 방식</strong>
    - 명령 전달: 개발자가 kubectl을 통해 YAML 파일(선언적 명세)을 API Server에 전달합니다.
    - 상태 기록: API Server는 이 명세를 etcd에 기록합니다.
    - 컨트롤러 작동: Controller Manager는 etcd에 기록된 &#39;원하는 상태(예: Pod 3개)&#39;와 &#39;현재 상태&#39;를 지속적으로 비교합니다.
    - 스케줄링: 개수가 부족하면 Scheduler가 어느 워커 노드에 Pod를 띄울지 결정합니다.
    - 실행 지시: API Server가 해당 노드의 kubelet에게 Pod 생성을 요청합니다.
    - 현지 관리: kubelet은 컨테이너 런타임(Docker 등)을 호출해 컨테이너를 실행하고, 그 상태를 다시 API Server에 보고합니다.</p>
<p>** 2. 서비스(Service)를 통한 Pod 접근 방식**
    - 객체 생성: 사용자가 Service 리소스를 생성하면 고정된 ClusterIP가 할당됩니다.
    - 엔드포인트(Endpoint) 관리: Service는 레이블 셀렉터(Label Selector)를 사용하여 대응하는 Pod들의 IP 목록을 자동으로 관리합니다.
    - kube-proxy의 역할: 모든 워커 노드에는 kube-proxy라는 네트워크 관리자가 상주합니다.
    - 트래픽 라우팅: 사용자가 Service IP로 접속하면, 각 노드의 kube-proxy가 설정한 네트워크 규칙(iptables/IPVS)에 따라 실제 살아있는 Pod 중 하나로 트래픽을 전달(Load Balancing)합니다.
    - 추상화: 사용자는 Pod가 삭제되고 새로 생성되어 IP가 바뀌어도, 고정된 Service IP만 바라보면 되므로 통신이 단절되지 않습니다.</p>
</blockquote>
</br>
</br>

<h2 id="배포-과정">배포 과정</h2>
<ol>
<li>Containerize (Dockerize): 내 Spring Boot 프로젝트를 Docker 이미지로 만듦 (JAR 파일을 포함한 실행 파일)</li>
<li>Image Push: 빌드한 이미지를 <strong>이미지 저장소(Docker Hub, AWS ECR, Azure ACR)</strong>에 업로드</li>
<li>Manifest 작성 (YAML): &quot;어떤 이미지를 사용할지&quot;, &quot;파드를 몇 개 띄울지&quot; 등을 담은 <strong>설정 파일(YAML)</strong>을 작성</li>
<li>Deployment 실행: kubectl apply -f deployment.yaml 명령어로 K8s에게 배포를 명령</li>
<li>Scheduling: K8s가 클러스터 내의 여러 노드 중 여유가 있는 노드에 Pod를 생성</li>
<li>Expose (Service): 외부 사용자가 접속할 수 있도록 Service와 <strong>Ingress(Gateway 역할)</strong>를 설정</li>
</ol>
</br>
</br>

<h2 id="테스트">테스트</h2>
<ul>
<li><a href="https://killercoda.com/playgrounds/kubernetes">Killercoda</a></li>
</ul>
</br>
</br>


<h3 id="reference">reference</h3>
<blockquote>
<ul>
<li><a href="https://kubernetes.io/ko/docs/tutorials/kubernetes-basics/">k8s 기초</a>
<a href="https://velog.io/@pinion7/Kubernetes-%EB%A6%AC%EC%86%8C%EC%8A%A4-Namespace%EC%97%90-%EB%8C%80%ED%95%B4-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B3%A0-%EC%8B%A4%EC%8A%B5%ED%95%B4%EB%B3%B4%EA%B8%B0">https://velog.io/@pinion7/Kubernetes-%EB%A6%AC%EC%86%8C%EC%8A%A4-Namespace%EC%97%90-%EB%8C%80%ED%95%B4-%EC%9D%B4%ED%95%B4%ED%95%98%EA%B3%A0-%EC%8B%A4%EC%8A%B5%ED%95%B4%EB%B3%B4%EA%B8%B0</a></li>
<li><a href="https://velog.io/@sgwon1996/%EC%BF%A0%EB%B2%84%EB%84%A4%ED%8B%B0%EC%8A%A4-%ED%99%98%EA%B2%BD%EC%97%90-%EC%8A%A4%ED%94%84%EB%A7%81-%EC%96%B4%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98-%EB%B0%B0%ED%8F%AC%ED%95%98%EA%B8%B0">https://velog.io/@sgwon1996/%EC%BF%A0%EB%B2%84%EB%84%A4%ED%8B%B0%EC%8A%A4-%ED%99%98%EA%B2%BD%EC%97%90-%EC%8A%A4%ED%94%84%EB%A7%81-%EC%96%B4%ED%94%8C%EB%A6%AC%EC%BC%80%EC%9D%B4%EC%85%98-%EB%B0%B0%ED%8F%AC%ED%95%98%EA%B8%B0</a></li>
<li><a href="https://kubernetes.io/docs/concepts/services-networking/service/">공식문서 service</a></li>
<li><a href="https://seongjin.me/kubernetes-service-types/">service</a></li>
<li><a href="https://hoing.io/archives/111">service 이미지</a></li>
<li><a href="https://tech.hancom.com/k8s-tips-tricks/">실전 적용 시 pod가 죽지 않을 경우</a></li>
</ul>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[JAVA] Future, CompletableFuture]]></title>
            <link>https://velog.io/@ilov-/JAVA-Future-CompletableFuture</link>
            <guid>https://velog.io/@ilov-/JAVA-Future-CompletableFuture</guid>
            <pubDate>Wed, 10 Jul 2024 14:29:00 GMT</pubDate>
            <description><![CDATA[<h1 id="future">Future</h1>
<ul>
<li><p>자바 5부터 도입된 비동기 작업의 결과를 나타내는 객체</p>
</li>
<li><p>ExecutorService.submit() 같은 걸로 비동기 작업을 실행하면, 그 결과로 Future 객체가 리턴됨</p>
</li>
<li><p>단점    </p>
<ul>
<li>결과를 얻으려면 future.get()을 블로킹해서 기다려야 함</li>
<li>작업이 끝났는지 알기 위해 isDone()을 계속 확인해야 함.</li>
<li>콜백 등록이 안 됨 → 작업 완료 후 자동으로 뭔가를 실행하는 기능이 없음</li>
</ul>
</li>
</ul>
<pre><code class="language-java">ExecutorService executor = Executors.newSingleThreadExecutor();

Future&lt;String&gt; future = executor.submit(() -&gt; {
    Thread.sleep(2000); // 2초 기다리기
    return &quot;커피 완성!&quot;;
});

System.out.println(&quot;커피 주문 완료! 기다리는 중...&quot;);

String result = future.get(); // ⚠️ 여기가 블로킹. 2초 동안 멈춤.
System.out.println(result);   // 출력: 커피 완성!

executor.shutdown();
</code></pre>
<ul>
<li>future.get()이 작업이 끝날 때까지 기다려야 함</li>
<li>기다리는 동안 다른 작업 못 함.</br>

</li>
</ul>
<h1 id="completablefuture">CompletableFuture</h1>
<ul>
<li><p>자바 8부터 등장한 업그레이드 버전 Future</p>
</li>
<li><p>특징</p>
<ul>
<li>비동기 실행 및 콜백 지원</li>
<li>결과 조합 (thenCombine, thenApply, thenAccept 등)</li>
<li>예외 처리 용이 (exceptionally, handle 등)</li>
<li>멀티스레딩 쉽게 처리 가능</li>
</ul>
</li>
</ul>
<pre><code class="language-java">
// 비동기 + 콜백
CompletableFuture&lt;String&gt; cf = CompletableFuture.supplyAsync(() -&gt; {
    try {
        Thread.sleep(2000); // 2초 대기
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return &quot;커피 완성!&quot;;
});

System.out.println(&quot;커피 주문 완료! 기다리는 동안 다른 일 하기...&quot;);

cf.thenAccept(result -&gt; {
    System.out.println(&quot;✅ 받은 메시지: &quot; + result); // 출력: 커피 완성!
});

System.out.println(&quot;👉 다른 일 수행 중...&quot;);

// cf가 끝날 때까지 메인 쓰레드가 너무 빨리 끝나지 않도록 잠깐 대기
Thread.sleep(3000);


// 출력
// 커피 주문 완료! 기다리는 동안 다른 일 하기...
// 👉 다른 일 수행 중...
// ✅ 받은 메시지: 커피 완성!</code></pre>
<h3 id="reference">Reference</h3>
<blockquote>
<p><a href="https://devfunny.tistory.com/809">https://devfunny.tistory.com/809</a>
<a href="https://rudaks.tistory.com/entry/Future%EC%99%80-CompletableFuture#google_vignette">https://rudaks.tistory.com/entry/Future%EC%99%80-CompletableFuture#google_vignette</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Vue] Component]]></title>
            <link>https://velog.io/@ilov-/Vue-Component</link>
            <guid>https://velog.io/@ilov-/Vue-Component</guid>
            <pubDate>Thu, 23 May 2024 06:15:02 GMT</pubDate>
            <description><![CDATA[<h2 id="기초">기초</h2>
<p><a href="https://ko.vuejs.org/guide/essentials/component-basics.html">https://ko.vuejs.org/guide/essentials/component-basics.html</a></p>
<pre><code class="language-javascript">// ButtonCounter.vue
&lt;script&gt;
export default {
  data() {
    return {
      count: 0
    }
  }
}
&lt;/script&gt;

&lt;template&gt;
  &lt;button @click=&quot;count++&quot;&gt;
    당신은 {{ count }} 번 클릭했습니다.
  &lt;/button&gt;
&lt;/template&gt;
</code></pre>
<pre><code class="language-javascript">// App.vue
&lt;script&gt;
import ButtonCounter from &#39;./ButtonCounter.vue&#39;

export default {
  components: {
    ButtonCounter
  }
}
&lt;/script&gt;

&lt;template&gt;
    &lt;h1&gt;여기에 많은 하위 컴포넌트가 있습니다!&lt;/h1&gt;
    &lt;ButtonCounter /&gt;     // -&gt;  &lt;button&gt;당신은 0번 클릭했습니다.&lt;/button&gt;
    &lt;ButtonCounter /&gt;     // -&gt;  &lt;button&gt;당신은 0번 클릭했습니다.&lt;/button&gt;
    &lt;ButtonCounter /&gt;     // -&gt;  &lt;button&gt;당신은 0번 클릭했습니다.&lt;/button&gt;
&lt;/template&gt;</code></pre>
<p><a href="https://play.vuejs.org/#eNqNVMFq20AQ/ZVhKSihiV03ORnFpCkptIc2NKWXKgdFWjsby7tCu3KdGoMpBFrT3uLWgSS00EMPgebQg0v/yJb+obOSJctgTIWQNPPmzc6bnVWXPPL9UjukpEpM6QTMVzWLs5YvAgV7nmgcCKmgHogWGKVy5tAEw+IWp50k0KV1O/QUdC0O4Ahkc8qVrKYOyBNpq7ehn66t7LX1DA+oCgOeWQA+xiL7TWYDdIG5VahsgGLKo1UwXoc0uuxDPPw5+TuKh1cwfT+KrkcQfb2Nv3w0ZssUyQ8XyNPvSBlf4Gvy5zwejibju2WkrQXS5K4P0c3t9Nc4uvkwHVxAdP07uvqGxIx3lGfQGp4Irg7ZO2RXUncShw+8zXLebjQUbfmerShaAKbL2lCV6syjOxbpQj1PU0wK98GgLb02SVjIK/ZZX+3NuggwhaYB42lfLZLB1SY9m6El5hb8ieQMSYw5uEu5ZwcNuqloR81i5iXtwINSJQuumfnEpLrKKAy/zHIul2yQ4lAtTOGy4fID4evJMJKqjLTdtMWScTGKpRlH/9Vmx7OlRBnHWMWm1pJ001IIn2zXut10+6HXM8toz5DjUCnBYdfxmNNE8j1dwdri8uuYKD7/HA1+xIMxxJfD6ae+WU6ZK7qhpCN4nTVKp1JwbEei2iL6UDGPBi98xQTHPczPlkVszxNvnyU+FYR0NoHIOaFOc4n/VHa0zyIHAZU0aOPm5pjSArAHGt4/fI5CCmBLuKEehRXgSyqFF+oa07C9kLtYdiEuqfZp8oNhvPFK7ncU5TITpQud/yUsgjPxeIX0eblbpe3sbJHeP/F5rIQ=">온라인으로 연습하기</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Vue]]]></title>
            <link>https://velog.io/@ilov-/Vue</link>
            <guid>https://velog.io/@ilov-/Vue</guid>
            <pubDate>Mon, 06 May 2024 14:00:57 GMT</pubDate>
            <description><![CDATA[<p>vue 
vuetify
router
decorator</p>
<h2 id="템플릿-문법">템플릿 문법</h2>
<h3 id="javascript-표현식-사용">Javascript 표현식 사용</h3>
<ul>
<li>Vue 템플릿에서 JavaScript 표현식은 다음과 같은 위치에 사용할 수 있습니다:</li>
</ul>
<pre><code>- 이중 중괄호(텍스트 보간법) 내부

- 모든 Vue 디렉티브 속성(v-로 시작하는 특수 속성) 내부</code></pre><h3 id="디렉티브">디렉티브</h3>
<ul>
<li>v- 접두사가 있는 특수한 속성 </li>
<li>Vue는 v-html과 v-bind를 포함하여 빌트인 디렉티브를 제공</li>
<li>디렉티브 속성 값은 단일 JavaScript 표현식이어야 함 
(v-for, v-on 및 v-slot 제외). </li>
<li>디렉티브의 역할은 표현식 값이 변경될 때 DOM에 반응적으로 업데이트를 적용하는 것</li>
</ul>
<pre><code class="language-vue">&lt;a v-bind:href=&quot;url&quot;&gt; ... &lt;/a&gt;

&lt;!-- 단축 문법 --&gt;
&lt;a :href=&quot;url&quot;&gt; ... &lt;/a&gt;


&lt;a v-on:click=&quot;doSomething&quot;&gt; ... &lt;/a&gt;

&lt;!-- 단축 문법 --&gt;
&lt;a @click=&quot;doSomething&quot;&gt; ... &lt;/a&gt;</code></pre>
<p><img src="https://velog.velcdn.com/images/ilov-/post/2591f381-e952-4933-b639-8f07d5e04751/image.png" alt=""></p>
<h3 id="계산된-속성">계산된 속성</h3>
<h3 id="컴포넌트">컴포넌트</h3>
<p><img src="https://velog.velcdn.com/images/ilov-/post/5b22ecc3-3fef-4520-a149-a4aca12cf697/image.png" alt=""></p>
<p>Vue.js에서 emit과 prop은 Vue 컴포넌트 간에 데이터 흐름을 조절하는 중요한 메커니즘입니다.</p>
<p>emit: Vue 컴포넌트에서 사용되는 이벤트를 발생시키는 메소드입니다. 자식 컴포넌트에서 이벤트를 발생시키고, 부모 컴포넌트에서 해당 이벤트를 감지하여 처리할 수 있습니다. emit 메소드를 사용하여 자식 컴포넌트에서 이벤트를 발생시키고, 부모 컴포넌트에서 v-on 디렉티브를 사용하여 해당 이벤트를 수신합니다.
prop: Vue 컴포넌트 간에 데이터를 전달하는데 사용되는 속성(properties)입니다. 부모 컴포넌트에서 자식 컴포넌트로 데이터를 전달할 때 사용됩니다. 부모 컴포넌트에서 자식 컴포넌트를 선언할 때 v-bind 디렉티브를 사용하여 prop에 데이터를 전달합니다. 자식 컴포넌트에서는 이러한 prop을 받아 사용할 수 있습니다.
이러한 메커니즘을 통해 Vue 컴포넌트 간에 데이터를 효율적으로 전달하고 상호작용할 수 있습니다.</p>
<hr>
<h3 id="reference">Reference</h3>
<blockquote>
<p><a href="https://ko.vuejs.org/guide/essentials/template-syntax.html">https://ko.vuejs.org/guide/essentials/template-syntax.html</a></p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Spring] @PreAuthorize]]></title>
            <link>https://velog.io/@ilov-/Spring-PreAuthorize</link>
            <guid>https://velog.io/@ilov-/Spring-PreAuthorize</guid>
            <pubDate>Mon, 01 Apr 2024 01:08:07 GMT</pubDate>
            <description><![CDATA[<h2 id="사용법">사용법</h2>
<ul>
<li><p>Configure class에 @EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true) 추가</p>
<pre><code class="language-java">@EnableGlobalMethodSecurity(securedEnabled = true, prePostEnabled = true)
public class ServerSecurityConfiguration extends WebSecurityConfigurerAdapter {

  private final LoginService loginService;

  @Override
  protected void configure(HttpSecurity http) throws Exception {

      //.....
 }
}</code></pre>
</li>
<li><p>원하는 controller의 메소드에 애노테이션 설정</p>
<pre><code class="language-java">@PostAuthorize(&quot;isAuthenticated() and (( returnObject.name == principal.name ) or hasRole(&#39;ROLE_ADMIN&#39;))&quot;)
@RequestMapping( value = &quot;/{id}&quot;, method = RequestMethod.GET )
public Project getProject( @PathVariable(&quot;id&quot;) long id ){
  return service.findOne(id);
}</code></pre>
</li>
</ul>
<p>-- 출처 <a href="https://joomn11.tistory.com/88">https://joomn11.tistory.com/88</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[redis] redisson  분산락]]></title>
            <link>https://velog.io/@ilov-/redis-redisson-%EB%B6%84%EC%82%B0%EB%9D%BD</link>
            <guid>https://velog.io/@ilov-/redis-redisson-%EB%B6%84%EC%82%B0%EB%9D%BD</guid>
            <pubDate>Wed, 10 Jan 2024 07:19:27 GMT</pubDate>
            <description><![CDATA[<p><a href="https://kkambi.tistory.com/196">https://kkambi.tistory.com/196</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[개발 서적] 함수형 코딩 Grokking Simplicity - 3]]></title>
            <link>https://velog.io/@ilov-/%EA%B0%9C%EB%B0%9C-%EC%84%9C%EC%A0%81-%ED%95%A8%EC%88%98%ED%98%95-%EC%BD%94%EB%94%A9-Grokking-Simplicity-3</link>
            <guid>https://velog.io/@ilov-/%EA%B0%9C%EB%B0%9C-%EC%84%9C%EC%A0%81-%ED%95%A8%EC%88%98%ED%98%95-%EC%BD%94%EB%94%A9-Grokking-Simplicity-3</guid>
            <pubDate>Sun, 07 Jan 2024 12:12:49 GMT</pubDate>
            <description><![CDATA[<h1 id="ii-일급-추상">II. 일급 추상</h1>
<h2 id="ch16-타임라인-사이에-자원-공유하기">ch16. 타임라인 사이에 자원 공유하기</h2>
<ul>
<li>자원을 공유하지 않는 타임라인이 가장 좋음</li>
<li>자원을 공유해야 한다면 안전하게 공유하기 위해 동시성 기본형(concurrency primitive)이라는 재사용 가능한 코드를 만들어 사용</li>
</ul>
<blockquote>
<h3 id="좋은-타임-라인의-원칙">좋은 타임 라인의 원칙</h3>
<ol>
<li>타임라인은 적을수록 이해하기 쉬움 (가능한 실행 순서의 개수 공식)</li>
<li>타임라인은 짧을수록 이해하기 쉬움</li>
<li>공유하는 자원이 적을수록 이해하기 쉬움</li>
<li>자원을 공유한다면 서로 조율해야 함</li>
<li>시간을 일급을 다룸</li>
</ol>
</blockquote>
<blockquote>
<p>동시성 기본형</p>
</blockquote>
<ul>
<li>자원을 안전하게 공유할 수 있는 재사용 가능한 코드를 말함</li>
</ul>
</br>
</br>

<h2 id="ch17-타임라인-조율하기">ch17. 타임라인 조율하기</h2>
<ul>
<li><p>위의 5번째 원칙에 따라 시간을 다룰 수 있는 대상으로 생각하기</p>
</li>
<li><p>타임라인을 나누기 위한 동시성 기본형</p>
<ul>
<li>여러 타임라인이 다른 시간에 종료되어도 서로 기다릴 수 있는 간단하고 재사용 가능한 기본형 필요</li>
</ul>
</li>
<li><p>cut() 동시성 기본형 </p>
<ul>
<li><p>어떤 타임라인 작업이 끝났을 때 이 함수를 부름</p>
</li>
<li><p>이 함수는 호출될 때마다 호출된 횟수를 증가시킴</p>
</li>
<li><p>마지막 타임라인이 함수를 호출했을 때 콜백을 불러줌</p>
<pre><code class="language-javascript">function Cut(num, callback) {
var num_finished = 0;

return function() {
  num_finished += 1;
    if (num_finished === num)
    callback();
};
}
</code></pre>
</li>
</ul>
</li>
</ul>
<p>// 예제
var done = Cut(3, function() {
    console.log(&quot;3 timelines are finished&quot;);
});</p>
<p>done();
done();
done();</p>
<p>console =&gt; &quot;3 timelines are finished&quot;</p>
<pre><code>
`코드에 Cut() 적용하기`
```javascript
/// Before
function calc_cart_total(cart, callback) {
  var total = 0;
  cost_ajax(cart, function(cost) {
    total += cost;
  });

  shipping_ajax(cart, function(shipping) {
    total += shipping;
    callback(total);
  });
}


/// With Cut()
function calc_cart_total(cart, callback) {
  var total = 0;
  var done = Cut(2, function() {   // done()이 두번 호출될때까지
    callback(total);
  });
  cost_ajax(cart, function(cost) {
    total += cost;
    done();
  });
  shipping_ajax(cart, function(shipping) {
    total += shipping;
    done();
  });
}
</code></pre><ul>
<li>타임 라인 사진 캡쳐 (p.489)</li>
</ul>
]]></description>
        </item>
    </channel>
</rss>