<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>gdh_.log</title>
        <link>https://velog.io/</link>
        <description></description>
        <lastBuildDate>Tue, 28 May 2024 10:25:33 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>gdh_.log</title>
            <url>https://velog.velcdn.com/images/gdh_/profile/86929b0f-3c96-4f4f-a983-09f3ef4cc706/social_profile.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. gdh_.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/gdh_" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[[Java] : Stream]]></title>
            <link>https://velog.io/@gdh_/Java-Stream</link>
            <guid>https://velog.io/@gdh_/Java-Stream</guid>
            <pubDate>Tue, 28 May 2024 10:25:33 GMT</pubDate>
            <description><![CDATA[<h1 id="java--stream">[Java] : Stream</h1>
<hr>
<h2 id="stream이란">Stream이란?</h2>
<p>Java 8부터 추가된 기술인 Stream API는 ‘일련의 데이터의 흐름’을 표준화된 방법으로 쉽게 처리할 수 있도록 지원하는 ‘클래스의 집합(패키지)’이다. </p>
<p>오라클 공식 문서에서는 Stream 패키지를 ‘요소들의 Stream에 함수형 연산(람다함수를 통한 연산)을 지원하는 클래스’라고 정의하고 있다. </p>
<p>즉, Java의 Stream을 이용하면 일련의 데이터를 함수형 연산을 통해 표준화된 방법으로 쉽게 가공, 처리할 수 있다.</p>
<h2 id="stream-장점">Stream 장점</h2>
<p>Stream 사용 이전에는 데이터 처리에 주로 Collection을 이용했다.</p>
<p>Stream은 Collection과 기존 for문을 사용해 데이터를 처리할 때와 비교했을 때 코드가 훨씬 간결해지고 명료해져 코드의 <strong>가독성</strong>과 <strong>유지보수성</strong>을 높여준다.</p>
<p>또한 Stream은 <strong>병렬처리</strong>를 지원하고 있기 때문에 대용량의 데이터를 효율적으로 처리 가능하다.</p>
<pre><code class="language-java">class Fruit {
    String name; // 이름
    String origin; // 원산지
    Integer price; // 가격
    Integer amount; // 재고

    public Fruit(String name, String origin, Integer price, Integer amount) {
        this.name = name;
        this.origin =  origin;
        this.price = price;
        this.amount = amount;
    }
}

List&lt;Fruit&gt; fruits = Arrays.asList(
        new Fruit(&quot;cherry&quot;, &quot;A&quot;, 2000, 3),
        new Fruit(&quot;banana&quot;, &quot;C&quot;, 3500, 5),
        new Fruit(&quot;avocado&quot;, &quot;B&quot;, 3000, 0),
        new Fruit(&quot;blueberry&quot;, &quot;B&quot;, 2000, 1),
        new Fruit(&quot;apple&quot;, &quot;A&quot;, 3000, 3),
        new Fruit(&quot;coconut&quot;, &quot;B&quot;, 3500, 6),
        new Fruit(&quot;tomato&quot;, &quot;C&quot;, 3000, 0));</code></pre>
<p>위와 같은 상황의 과일 리스트에서 과일을 원산지 별로 묶어 이름순 정렬한 리스트를 구하고, 원산지별 과일을 3개씩 구매했을 때의 가격 총합을 구하고 싶을 때 코드를 보자.</p>
<pre><code class="language-java">// 기존 방식
void findFruitsFor () {
    Map&lt;String, List&lt;Fruit&gt;&gt; fruitByOrigin = new HashMap&lt;&gt;();

    // 원산지별 나누기 
    for (Fruit fruit : fruits) {
        List&lt;Fruit&gt; value = fruitByOrigin.getOrDefault(fruit.origin, new ArrayList&lt;&gt;());
        value.add(fruit);
        fruitByOrigin.put(fruit.origin, value);
    }

    // 원산지별 나눈 과일 리스트 정렬
    fruitByOrigin.forEach((key, value) -&gt; {
        System.out.println(&quot;원산지: &quot; + key);
        Collections.sort(value, new Comparator&lt;Fruit&gt;() {
            @Override
            public int compare(Fruit o1, Fruit o2) {
                return o1.name.compareTo(o2.name);
            }});

        // 원산지별 과일 가격 총합 구하기
        int totalPrice = 0;
        for (Fruit fruit : value) {
            System.out.println(fruit.name);
            totalPrice += fruit.price * Math.min(3, fruit.amount);
        }
        System.out.println(&quot;가격 총합: &quot; + totalPrice);
        System.out.println();
    });
}</code></pre>
<pre><code class="language-java">void findFruitsStream () {
// Stream 사용

    // 원산지별 과일 나누고 정렬하기
    Map&lt;String, List&lt;Fruit&gt;&gt; fruitByOrigin = fruits.stream()
            .collect(groupingBy(fruit -&gt; fruit.origin, collectingAndThen(Collectors.toList(), list -&gt; {
                Collections.sort(list, new Comparator&lt;Fruit&gt;() {
                    @Override
                    public int compare(Fruit o1, Fruit o2) {
                        return o1.name.compareTo(o2.name);
                    }
                });
                return list;
            })));

    // 원산지별 과일 가격 총합 구하기
    Map&lt;String, Integer&gt; totalPrice = fruits.stream()
            .collect(groupingBy(fruit -&gt; fruit.origin, summingInt(fruit -&gt; fruit.price * Math.min(3, fruit.amount))));

    fruitByOrigin.forEach((key, value) -&gt; {
        System.out.println(&quot;원산지: &quot; + key);
        value.forEach(fruit -&gt; System.out.println(fruit.name));
        System.out.println();
    });
    System.out.println(totalPrice);
}</code></pre>
<p>Stream을 사용했을 때의 코드가 훨씬 간결하고 가독성이 높은 것을 확인할 수 있다. </p>
<h2 id="stream-과정">Stream 과정</h2>
<p>데이터를 처리하기 위해서는 데이터를 생성하고, 생성된 데이터를 가공해 필요한 형태로 변환한 후, 결과를 소비해야 한다. </p>
<p>따라서 Stream은 데이터 처리를 위한 API이므로 생성 -&gt; 가공 -&gt; 소비의 과정으로 구성되어 있다.</p>
<h2 id="1-생성">1. 생성</h2>
<ul>
<li><p>배열 스트림 : Arrays.stream()</p>
</li>
<li><p>컬렉션 스트림 : .stream()</p>
</li>
<li><p>Stream.builder()</p>
<pre><code class="language-java">Stream&lt;String&gt; builderStream = Stream.&lt;String&gt;builder()
  .add(&quot;a&quot;).add(&quot;b&quot;).add(&quot;c&quot;)
  .build(); </code></pre>
</li>
<li><p>Stream.generate(), iterate()</p>
<pre><code class="language-java">Stream&lt;String&gt; generatedStream = Stream.generate(() -&gt; &quot;a&quot;).liimit(3);
// 생성할 때 스트림의 크기가 정해져있지 않기(무한하기)때문에 최대 크기를 제한해줘야 한다.
</code></pre>
</li>
</ul>
<p>Stream<Integer> iteratedStream = Stream.iterate(0, n-&gt;n+2).limit(5);
//0,2,4,6,8</p>
<pre><code>
- 기본 타입형 스트림
```java
IntStream intStream = IntStream.range(1, 5); // [1, 2, 3, 4]</code></pre><ul>
<li>병렬 스트림<pre><code class="language-java">Stream&lt;String&gt; parallelStream = list.parallelStream();</code></pre>
</li>
</ul>
<h2 id="2-가공">2. 가공</h2>
<pre><code class="language-java">Stream&lt;T&gt; skip(long n) // n개의 요소를 건너뛴다.
Stream&lt;T&gt; limit(long maxSize) // 스트림의 요소를 maxSize개로 제한한다.

Stream&lt;T&gt; filter(Predicate&lt;? super T? predicate) // 조건(Predicate)에 맞지 않는 요소를 걸러낸다.
Stream&lt;T&gt; distinct() // 스트림에서 중복된 요소를 제거한다.

Stream&lt;T&gt; sorted(Comparator&lt;? super T&gt; comparator) // 지정된 Comparator에 따라 정렬한다.

Stream&lt;R&gt; map(Function&lt;? super T, ? extends R&gt; mapper) // 스트림의 요소를 특정 형태로 변환한다.

// Stream&lt;T&gt; 타입의 스트림을 기본형 스트림으로 변환한다.
IntStream mapToInt(ToIntFunction&lt;? super T&gt; mapper)
LongStream mapToLong(ToLongFunction&lt;? super T&gt; mapper)
DoubleStream mapToDouble(ToDoubleFunction&lt;? super T&gt; mapper)</code></pre>
<h2 id="3-소비">3. 소비</h2>
<pre><code class="language-java">IntStream stream = list.stream()
    .count()     // 스트림 요소 개수 반환 
    .sum()        // 스트림 요소의 합 반환
    .min()        // 스트림 최소값 반환
    .max()        // 스트림 최대값 반환
    .average()    // 스트림 평균값 반환
-----------------------------------------------------------------------------------------
// reduce - 스트림의 요소를 하나씩 줄여가며 누적연산 수행
IntStream.range(1,5)
    .reduce(10, (total,num)-&gt;total+num);
    //reduce(초기값, (누적 변수,요소)-&gt;수행문)
    // 10 + 1+2+3+4+5 = 25

-----------------------------------------------------------------------------------------
// collect - 스트림을 원하는 자료형으로 변환
List&lt;Person&gt; members = Arrays.asList(new Person(&quot;lee&quot;,26),
                                     new Person(&quot;kim&quot;, 23),
                                     new Person(&quot;park&quot;, 23));

// toList() - 리스트로 반환
members.stream()
    .map(Person::getLastName)
    .collect(Collectors.toList());

// joining() - 작업 결과를 하나의 스트링으로 이어 붙이기
members.stream()
    .map(Person::getLastName)
    .collect(Collectors.joining(delimiter = &quot;+&quot; , prefix = &quot;&lt;&quot;, suffix = &quot;&gt;&quot;);
    // &lt;lee+kim+park&gt;

//groupingBy() - 그룹지어서 Map으로 반환
members.stream()
    .collect(Collectors.groupingBy(Person::getAge));
    // {26 = [Person{lastName=&quot;lee&quot;,age=26}],
    //  23 = [Person{lastName=&quot;kim&quot;,age=23},Person{lastName=&quot;park&quot;,age=23}]}

//collectingAndThen() - collecting 이후 추가 작업 수행
members.stream()
    .collect(Collectors.collectingAndThen (Collectors.toSet(),
                                           Collections::unmodifiableSet));
    //Set으로 collect한 후 수정불가한 set으로 변환하는 작업 실행

-----------------------------------------------------------------------------------------
List&lt;String&gt; members = Arrays.asList(&quot;Lee&quot;, &quot;Park&quot;, &quot;Hwang&quot;);
boolean matchResult = members.stream()
                        .anyMatch(members-&gt;members.contains(&quot;w&quot;)); //w를 포함하는 요소가 있는지, True

boolean matchResult = members.stream()
                        .allMatch(members-&gt;members.length() &gt;= 4); //모든 요소의 길이가 4 이상인지, False

boolean matchResult = members.stream()
                        .noneMatch(members-&gt;members.endsWith(&quot;t&quot;)); //t로 끝나는 요소가 하나도 없는지, True</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Spring] : Autowiring 과정]]></title>
            <link>https://velog.io/@gdh_/Spring-Autowiring-%EA%B3%BC%EC%A0%95</link>
            <guid>https://velog.io/@gdh_/Spring-Autowiring-%EA%B3%BC%EC%A0%95</guid>
            <pubDate>Wed, 22 May 2024 09:56:35 GMT</pubDate>
            <description><![CDATA[<h1 id="spring--autowiring-과정">[Spring] : Autowiring 과정</h1>
<hr>
<h2 id="autowired란">@Autowired란?</h2>
<p>@Autowired는 의존 관계 주입을 할 때 사용하는 어노테이션이며, 의존 객체의 타입에 해당하는 빈을 찾아 주입하는 역할을 한다.</p>
<h2 id="빈을-찾는-과정">빈을 찾는 과정</h2>
<p>빈의 인스턴스가 만들어지는 라이프 사이클 중, <strong>BeanPostProcessor는</strong> 빈의 초기화 단계 이전과 이후 또 다른 부가적인 작업을 할 수 있게 하는 인터페이스이다.</p>
<p>빈에 접근해야 하기 때문에 빈을 관리하는 스프링 컨테이너에 등록이 되어 있다.</p>
<p><img src="https://velog.velcdn.com/images/gdh_/post/45bd2efd-8970-4d72-ad50-d18aa1095762/image.png" alt=""></p>
<p>BeanPostProcessor를 보면 빈의 초기화 이전과 이후에 해당하는 postProcessBeforeInitialization 메소드와 postProcessAfterInitialization 메소드가 존재하는 것을 확인할 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/gdh_/post/3edad1cf-4d0c-45a6-9cd7-a942aba49306/image.png" alt=""></p>
<p>위에서 설명한 BeanPostProcessor도 구현체가 있어야 메소드를 이용할 수 있다.</p>
<p>@Autowired에 있어서 핵심이 되는 클래스는 <strong>AutowiredAnnotationBeanPostProcessor</strong>이고, 이것 또한 빈에 접근해야 되기 때문에 빈을 관리하는 스프링 컨테이너에 등록되어 있다.</p>
<p>그리고 빈의 초기화 라이프 사이클 이전에 @Autowired가 붙은 빈을 찾아 주입하는 역할을 한다.</p>
<p><img src="https://velog.velcdn.com/images/gdh_/post/e5b44fe2-2a72-437b-9ba1-4e343358fe31/image.png" alt=""></p>
<p>이 processInjection 메소드를 사용해 빈의 클래스 정보를 읽어와 자동으로 의존 관계를 설정할 메타데이터를 얻고 주입한다.</p>
<p><img src="https://velog.velcdn.com/images/gdh_/post/49044d86-dcbe-4941-952a-5e40204b9ac0/image.png" alt=""></p>
<p>그리고 위의 inject 메소드가 객체를 주입할 때 ReflectionUtils 클래스를 사용하는 것을 볼 수 있다.</p>
<p>즉, @Autowired는 리플렉션(구체적인 클래스 타입을 알지 못하더라도 그 클래스의 메서드, 타입, 변수들에 접근할 수 있도록 해주는 자바 API)을 통해 수행된다.</p>
<p>실제로 AutowiredAnnotationBeanPostProcessor는 injectMetadata를 상속받는 AutowiredFieldElement와 AutowiredMethodElement를 구현하며 필드나 메소드 각각에 맞게 inject 메소드가 호출된다.</p>
<h3 id="동작-순서">동작 순서</h3>
<ul>
<li>BeanFactory( ApplicationContext )가 BeanPostProcessor 타입의 Bean을 찾는다.</li>
<li>IoC 컨테이너에 등록되어있는 다른 일반적인 Bean에게 BeanPostProcessor를 적용한다.</li>
<li>다른 Bean에 @Autowired Annotation을 처리하는 AutowiredAnnotationBeanPostProcessor의 로직이 적용된다.</li>
<li>의존성 주입이 일어난다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Spring] : Spring Bean (개념, Scope, 생명 주기)]]></title>
            <link>https://velog.io/@gdh_/Spring-Spring-Bean</link>
            <guid>https://velog.io/@gdh_/Spring-Spring-Bean</guid>
            <pubDate>Tue, 14 May 2024 10:19:09 GMT</pubDate>
            <description><![CDATA[<h1 id="spring--spring-bean">[Spring] : Spring Bean</h1>
<p>빈(Bean)은 스프링 컨테이너에 의해 관리되는 재사용 가능한 소프트웨어 컴포넌트이다.</p>
<p>스프링 컨테이너가 관리하는 자바 객체를 의미하고, 컨테이너는 하나 이상의 빈을 관리한다.</p>
<p>빈은 인스턴스화된 객체이고, 스프링 컨테이너에 등록된 객체를 스프링 빈이라 한다.</p>
<hr>
<h2 id="spring-bean-등록-방법">Spring Bean 등록 방법</h2>
<p>스프링은 약속된 어노테이션이 붙어있는 클래스를 자동으로 인지한다.</p>
<p>또는 Bean Configuration File에 직접 빈을 등록해 사용할 수 있다.</p>
<h3 id="component">@Component</h3>
<p>스프링에서 관리하는 요소라는 의미의 일반적인 어노테이션이다.</p>
<h3 id="controller">@Controller</h3>
<p>Spring MVC의 컨트롤러 역할을 하는 클래스를 의미하는 어노테이션이다.</p>
<h3 id="restcontroller">@RestController</h3>
<p>@Controller와 @ResponseBody를 함께 사용한 조합의 어노테이션이다.</p>
<h3 id="service">@Service</h3>
<p>서비스 역할을 하는 클래스를 의미하는 어노테이션이다.</p>
<p>Controller는 들어온 요청을 처리하기 위해 Service를 호출하고, Service는 Controller로부터 받은 정보를 가지고 로직을 처리하는 방식으로 가공해 Controller에 다시 넘겨준다.</p>
<h3 id="repository">@Repository</h3>
<p>DAO 혹은 레포지토리 역할을 하는 클래스를 표시하는 어노테이션이다.</p>
<p>DB나 파일같은 외부 I/O에 접근하는 메소드를 사용하기 위한 인터페이스임을 의미할 수 있다.</p>
<h3 id="bean-configuration-file에-직접-등록">Bean Configuration File에 직접 등록</h3>
<pre><code class="language-java">@Configuration
public class HelloConfiguration {
    @Bean
    public HelloController sampleController() {
        return new SampleController;
    }
}</code></pre>
<hr>
<h2 id="spring-bean의-scope">Spring Bean의 Scope</h2>
<p>Bean Scope는 빈이 존재하는 범위를 뜻한다.</p>
<p>Bean 객체는 기본적으로 singleton의 범위를 가지며 singleton은 스프링 컨테이너의 시작과 종료까지 단 하나의 객체만을 사용하는 방식이다.</p>
<p>request, session, global session의 범위는 일반 Spring 어플리케이션이 아니라 Spring MVC Web 어플리케이션에서만 사용된다.</p>
<table>
<thead>
<tr>
<th align="center">범위</th>
<th align="center">설명</th>
</tr>
</thead>
<tbody><tr>
<td align="center">singleton</td>
<td align="center">하나의 Bean은 단 하나의 객체만 존재한다.</td>
</tr>
<tr>
<td align="center">prototype</td>
<td align="center">하나의 Bean은 다수의 객체가 존재할 수 있다.(의존성 관계의 Bean이 주입될 때마다 새로운 객체가 생성되어 주입된다.)</td>
</tr>
<tr>
<td align="center">request</td>
<td align="center">하나의 Bean은 하나의 HTTP request의 생명주기 안에 단 하나의 객체만 존재한다.</td>
</tr>
<tr>
<td align="center">session</td>
<td align="center">하나의 Bean은 하나의 HTTP Session의 생명주기 안에서 단 하나의 객체만 존재한다.</td>
</tr>
<tr>
<td align="center">global session</td>
<td align="center">하나의 Bean은 하나의 global HTTP Session의 생명주기 안에서 단 하나의 객체만 존재한다.</td>
</tr>
</tbody></table>
<hr>
<h2 id="spring-bean-생명-주기">Spring Bean 생명 주기</h2>
<ol>
<li>스프링 IoC 컨테이너 생성</li>
<li>스프링 빈 생성</li>
<li>의존관계 주입</li>
<li>초기화</li>
<li>사용</li>
<li>소멸</li>
<li>스프링 종료</li>
</ol>
<p>스프링 컨테이너가 초기화 될 때 빈 객체를 생성하고,</p>
<p>의존관계 주입 완료 후 빈 객체가 지정한 메소드를 호출해 초기화한다.</p>
<p>그리고 스프링 컨테이너가 종료되기 직전에도 빈이 지정한 메소드를 호출해 소멸 과정을 진행한다.</p>
<h3 id="bean-생명주기-콜백-방법">Bean 생명주기 콜백 방법</h3>
<p><strong>1. 인터페이스 (InitializingBean, DisposableBean)</strong></p>
<pre><code class="language-java">public class ExBean implements InitializingBean, DisposableBean {

    @Override
    public void afterPropertiesSet() throws Exception {
        // 초기화 콜백 (의존관계 주입이 끝나면 호출)
    }

    @Override
    public void destroy() throws Exception {
        // 소멸 전 콜백 (Bean 종료 전 메모리 반납, 연결 종료와 같은 과정)
    }
}</code></pre>
<p>빈 객체의 클래스가 InitializingBean, DisposableBean을 구현하며 해당 인터페이스에서 정의된 메소드를 호출해 빈 객체의 초기화 또는 종료를 수행한다.</p>
<p><strong>2. 설정 정보에서 초기화 메소드, 종료 메소드 지정</strong> </p>
<pre><code class="language-java">public class ExBean {

    public void initialize() throws Exception {
        // 초기화 콜백 (의존관계 주입이 끝나면 호출)
    }

    public void close() throws Exception {
        // 소멸 전 콜백 (메모리 반납, 연결 종료와 같은 과정)
    }
}

@Configuration
class LifeCycleConfig {

    @Bean(initMethod = &quot;initialize&quot;, destroyMethod = &quot;close&quot;)
    public ExBean exBean() {
        ...
    }
}</code></pre>
<p><strong>3. 어노테이션을 이용한 빈 초기화(@PostConstruct), 종료(@PreDestroy)</strong></p>
<pre><code class="language-java">import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

public class ExampleBean {

    @PostConstruct
    public void initialize() throws Exception {
        // 초기화 콜백 (의존관계 주입이 끝나면 호출)
    }

    @PreDestroy
    public void close() throws Exception {
        // 소멸 전 콜백 (메모리 반납, 연결 종료와 같은 과정)
    }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Spring] : Spring IoC/DI]]></title>
            <link>https://velog.io/@gdh_/Spring-Spring-DIIoC</link>
            <guid>https://velog.io/@gdh_/Spring-Spring-DIIoC</guid>
            <pubDate>Fri, 10 May 2024 10:28:46 GMT</pubDate>
            <description><![CDATA[<h1 id="spring--spring-iocdi">[Spring] : Spring IoC/DI</h1>
<hr>
<h2 id="ioc-inversion-of-control">IoC (Inversion of Control)</h2>
<p>IoC 제어의 역전이란, 메소드나 객체의 호출 작업을 개발자가 결정하는 게 아니라 외부에서 결정되는 것을 말한다.</p>
<p>객체의 의존성을 역전시켜 객체 간 결합도를 줄이고 유연한 코드를 작성 가능하게 함으로써 가독성과 코드 중복, 유지 보수를 편하게 할 수 있다.</p>
<p>기존과 다르게 스프링에서는 의존성 객체를 생성할 때 제어권을 스프링에 위임해 스프링이 만들어 놓은 객체를 주입함으로써 제어의 흐름을 사용자가 컨트롤 하는 것이 아니라 스프링에게 맡겨 작업을 처리한다.</p>
<h3 id="스프링-ioc-컨테이너">스프링 IoC 컨테이너</h3>
<p>스프링은 객체의 생성, 관계설정, 사용 제거 등의 작업을 애플리케이션과 독립된 컨테이너를 통해 실행한다.</p>
<p>우리가 new 키워드를 사용해 객체를 생성하고, 의존성을 주입하고, 연관관계를 맺었다면 스프링은 빈으로 등록하는 것 만으로 컨테이너에게 객체들을 관리받을 수 있다.</p>
<p>컨테이너는 여러 계층으로 이루어져 있는데, 빈을 생성하고 의존성을 주입하는 BeanFactory, 애플리케이션을 동작시키기 위한 ApplicationContext가 존재한다.</p>
<hr>
<h2 id="di-dependency-injection">DI (Dependency Injection)</h2>
<p>DI 의존성 주입이란, 스프링이 다른 프레임워크와 차별화되어 제공하는 의존 관계 주입 기능으로
객체를 직접 생성하는 게 아니라 외부에서 생성한 후 주입시켜주는 방식이다.</p>
<p>의존성 주입에는 세 가지 주요 스타일이 있다.</p>
<h3 id="생성자-주입">생성자 주입</h3>
<pre><code class="language-java">public class A {
    private final B b;

    @Autowired
    public A(B b) {
        this.b = b;
    }
}</code></pre>
<p>스프링에서 권장하는 방식이다.</p>
<h3 id="setter-주입">Setter 주입</h3>
<pre><code class="language-java">public class A {
    private final B b;

    @Autowired
    public void setB(B b) {
        this.b = b;
    }
}</code></pre>
<h3 id="필드-주입">필드 주입</h3>
<pre><code class="language-java">public class A {
    @Autowired
    private B b;
}</code></pre>
<p>가장 간단한 방법으로 변수에 @Autowired 붙여 사용한다.</p>
<p>대부분의 의존 관계는 어플리케이션 종료까지 변경될 일이 없다.</p>
<p>만약 setter 주입을 사용한다면 실수로라도 변경 가능성이 생기지만 생성자 주입은 호출 시점에 1번만 호출하기 때문에 불변으로 설계가 가능하다.</p>
<p>또한 단위 테스트시에 setter 주입은 임의의 관련 객체를 만들어야 하지만 누락 발생 가능하다.</p>
<p>하지만 생성자 주입을 사용하면 누락을 막을 수 있다.</p>
<p>이러한 이유들로 인해 생성자 주입을 권장하고 있다.</p>
<p>DI의 장점은 아래와 같다.</p>
<ul>
<li>의존성이 줄어들어 변경에 덜 취약해진다.</li>
<li>가독성이 높아진다.</li>
<li>재사용성이 높아진다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Java] : 자바에서의 null 처리]]></title>
            <link>https://velog.io/@gdh_/Java-%EC%9E%90%EB%B0%94%EC%97%90%EC%84%9C%EC%9D%98-null-%EC%B2%98%EB%A6%AC</link>
            <guid>https://velog.io/@gdh_/Java-%EC%9E%90%EB%B0%94%EC%97%90%EC%84%9C%EC%9D%98-null-%EC%B2%98%EB%A6%AC</guid>
            <pubDate>Wed, 08 May 2024 10:08:07 GMT</pubDate>
            <description><![CDATA[<h1 id="java--자바에서의-null-처리">[Java] : 자바에서의 null 처리</h1>
<hr>
<h2 id="null이란">Null이란?</h2>
<p>null은 변수가 가리키는 주소값이 없다는 것을 표현하기 위한 키워드이다. </p>
<p>하지만 자바에서는 int나 char와 같은 primitive 타입에는 초기화 이전에도 0이나 공백같은 원시값을 저장하고 있기 때문에 null을 대입할 수 없다.</p>
<p>반면에 reference 타입에는 값이 없거나 기본값을 null로 지정한다.</p>
<table>
<thead>
<tr>
<th align="center">자료형</th>
<th align="center">기본값</th>
</tr>
</thead>
<tbody><tr>
<td align="center">boolean</td>
<td align="center">false</td>
</tr>
<tr>
<td align="center">char</td>
<td align="center">&#39;\u0000&#39;</td>
</tr>
<tr>
<td align="center">byte,short,int</td>
<td align="center">0</td>
</tr>
<tr>
<td align="center">long</td>
<td align="center">0</td>
</tr>
<tr>
<td align="center">float</td>
<td align="center">0.0f</td>
</tr>
<tr>
<td align="center">double</td>
<td align="center">0.0d</td>
</tr>
<tr>
<td align="center">reference value</td>
<td align="center">null</td>
</tr>
</tbody></table>
<h2 id="npenullpointerexception">NPE(NullPointerException)</h2>
<p>에러 메시지 NullPointerException은 null 참조로 인해 자바 개발자들이 가장 골치아프게하는 1등 공신이다.</p>
<p>자바에서의 null은 참조가 없는 경우를 뜻하는데, 만약 null을 참조하는 레퍼런스 변수로 객체의 인스턴스 메서드를 호출하는 등의 객체 코드를 실행하려 할 때 NPE가 발생한다.</p>
<h3 id="npe-발생-시나리오">NPE 발생 시나리오</h3>
<pre><code class="language-java">public class Person {
    private Game game;
    private String name;

    public Person(String name) {
        this.name = name;
    }

    public Game getGame() {
        return game;
    }
}

public class Game {
    private Weapon weapon;

    public Weapon getWeapon() {
        return weapon;
    }
}

public class Weapon {
    public String printWeapon() {
        return &quot;sword&quot;;
    }
}

public static void main(String[] args) {
    Person person = new Person(&quot;aaa&quot;);

    person.getGame().getWeapon().printWeapon();
}</code></pre>
<p><img src="https://velog.velcdn.com/images/gdh_/post/4fae89e8-06a6-4584-8564-385c6a83afd1/image.png" alt=""></p>
<p>코드를 실행해보면 NPE가 발생하게 된다. </p>
<p>new Person()이 초기화 될 때, 인스턴스 객체인 Game에 값이 들어가지 않아 person.getGame()의 반환값이 null이기 때문에 null.getWeapon() 메서드가 작동하지 않기 때문이다.</p>
<h3 id="npe-방지하는-방법">NPE 방지하는 방법</h3>
<p>Java8 이전에는 중첩 조건문을 사용해 회피했다.</p>
<pre><code class="language-java">public static void main(String[] args) {
    Person person = new Person(&quot;aaa&quot;);

    Game game = person.getGame();
    if (game != null) {
        Weapon weapon = game.getWeapon();
        if (weapon != null) {
            String w = weapon.printWeapon();
        }
    }
}</code></pre>
<p>이러한 방식은 메서드가 많아질수록 if문 역시 증가되어 코드 가독성 측면에서 좋지 않은 방식이라 할 수 있다.</p>
<hr>
<h2 id="optional-클래스">Optional 클래스</h2>
<p>Java8이 등장하며 null 처리를 정식으로 지원하는 java.util.Optional 클래스가 추가되었다.</p>
<pre><code class="language-java">public final class Optional&lt;T&gt; {

       /**
       *if non-null, the value, if null, indicates no value is present
       */
       private final T value;
}</code></pre>
<p>Optional 클래스는 존재할수도 하지않을수도 있는 객체, null이 될 수도 있는 객체를 감싸는 래퍼 클래스이다.</p>
<pre><code class="language-java">public static void main(String[] args) {
    Person person = new Person(&quot;aaa&quot;);

    Optional&lt;String&gt; optionalWeapon = Optional.ofNullable(person)
                .map(Person::getGame)
                .map(Game::getWeapon)
                .map(Weapon::printWeapon);
}</code></pre>
<p>Optional을 사용하면 이전의 중첩 조건문을 위와 같이 간단하게 바꿀 수 있다.</p>
<h3 id="optional의-단점">Optional의 단점</h3>
<ul>
<li>NullPointerException 대신 NoSuchElementException이 발생할 수 있다.</li>
<li>Optional을 남용하면 코드의 가독성이 오히려 떨어질 수 있다.</li>
<li>시간적, 공간적 비용이 증가할 수 있다.</li>
</ul>
<h3 id="올바른-optional-사용법">올바른 Optional 사용법</h3>
<ul>
<li>Optional 변수에 null을 할당하지 마라</li>
<li>값이 없을 때 Optional.orElseX()로 반환하라</li>
<li>단순히 값을 얻으려는 목적으로만 Optional을 사용하지 마라</li>
<li>생성자, 수정자, 메서드 파라미터 등으로 Optional을 넘기지 마라</li>
<li>Collection의 경우 Optional이 아닌 빈 Collection을 사용하라</li>
<li>반환 타입으로만 사용하라</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[Hash]]></title>
            <link>https://velog.io/@gdh_/Hash</link>
            <guid>https://velog.io/@gdh_/Hash</guid>
            <pubDate>Wed, 01 May 2024 10:34:50 GMT</pubDate>
            <description><![CDATA[<h1 id="hash">Hash</h1>
<hr>
<h2 id="hash-1">Hash?</h2>
<p><img src="https://velog.velcdn.com/images/gdh_/post/982f746c-2ec0-4950-a846-d30ceb203414/image.png" alt=""></p>
<p>해시는 임의의 길이를 가진 데이터를 고정된 길이의 데이터로 매핑하는 것을 말한다.</p>
<p>이 과정에서 원본 데이터를 키(Key), 매핑하는 과정을 해싱(Hashing), 결과 데이터를 해시값(Hash Value)이라한다.</p>
<p>그리고 이렇게 데이터를 해싱하는 함수를 해시 함수(Hash Function)라 한다.</p>
<ul>
<li><strong>키</strong> : 매핑 전 원래 데이터의 값</li>
<li><strong>해시값</strong> : 매핑 후 데이터의 값</li>
<li><strong>해시 함수</strong> : 데이터를 해싱하는 함수</li>
</ul>
<h3 id="hash의-특징">Hash의 특징</h3>
<ul>
<li>임의의 길이 데이터로부터 고정된 길이 해시값을 얻는다.</li>
<li>단방향성이므로 해시값으로부터 키를 역산할 수 없다.</li>
<li>하나의 키 값에 하나의 해시값을 가져야 한다.</li>
</ul>
<hr>
<h2 id="hash-collision">Hash collision</h2>
<p>해시 함수는 해시값의 보통 입력의 범위보다 출력 값의 범위가 좁은 경우가 많기 때문에 <strong>서로 다른 두 개의 키에 대해 동일한 해시값</strong>을 내는 <strong>해시 충돌</strong>이 발생할 수 있다. </p>
<p>이를 해결하기 위한 방법에는 분리 연결법과 개방 주소법이 존재한다.</p>
<h3 id="분리-연결법separate-chaining">분리 연결법(Separate Chaining)</h3>
<p>분리 연결법은 충돌이 발생하면 Linked List를 사용해 연결된 새로운 공간을 사용하는 방법이다.</p>
<p><img src="https://velog.velcdn.com/images/gdh_/post/9dd0348d-eaae-4f29-8ec1-3e0aed7424fd/image.png" alt=""></p>
<h3 id="장점">장점</h3>
<ul>
<li>연결 리스트를 사용하기 때문에 보다 유연하게 사용가능하고, 해시값을 그대로 사용할 수 있다.</li>
</ul>
<h3 id="단점">단점</h3>
<ul>
<li><p>하지만 연결 리스트를 사용하기 때문에 추가적인 공간이 필요하고 메모리 문제가 발생할 수 있다.</p>
</li>
<li><p>연결 리스트는 검색 시 O(n) 걸리므로 같은 해시값을 갖는 데이터가 많아진다면 해시를 사용하는 이유가 사라진다.</p>
</li>
</ul>
<h3 id="개방-주소법open-addressing">개방 주소법(Open Addressing)</h3>
<p>개방 주소법은 충돌이 발생하면 다른 버킷을 사용하는 방법이다.</p>
<p><img src="https://velog.velcdn.com/images/gdh_/post/e3fd62b8-e8ab-4e23-8341-4975f6301bdc/image.png" alt=""></p>
<p>다른 버킷을 찾는 방법에는 대표적으로 3가지가 존재한다.</p>
<ol>
<li><strong>선형 탐색(Linear Probing)</strong> : 충돌 시 다음 버킷, 혹은 몇 개를 건너뛰어 데이터를 삽입한다.</li>
<li><strong>제곱 탐색(Quadratic Probing)</strong> : 충돌 시 제곱만큼 건너뛴 버킷에 데이터를 삽입한다.(1,4,9,16..)</li>
<li><strong>이중 해시(Double Hashing)</strong> : 충돌 시 다른 해시함수를 한 번 더 적용한 결과를 이용한다.</li>
</ol>
<h3 id="장점-1">장점</h3>
<ul>
<li><p>고정된 크기의 배열을 사용하므로 추가적인 공간을 필요로 하지 않는다.</p>
</li>
<li><p>데이터가 적을 때는 연속된 공간에서 빠르게 찾을 수 있기 때문에 분리 연결법보다 좋은 성능을 보인다.</p>
</li>
</ul>
<h3 id="단점-1">단점</h3>
<ul>
<li>데이터가 많아지면 충돌이 발생했을 때 다음 빈 버킷을 찾기 어려워지고, 값이 자주 추가되고 삭제되면 성능이 떨어진다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Java] : Mutable 객체와 Immutable 객체의 차이점]]></title>
            <link>https://velog.io/@gdh_/Java-Mutable-%EA%B0%9D%EC%B2%B4%EC%99%80-Immutable-%EA%B0%9D%EC%B2%B4%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%A0%90</link>
            <guid>https://velog.io/@gdh_/Java-Mutable-%EA%B0%9D%EC%B2%B4%EC%99%80-Immutable-%EA%B0%9D%EC%B2%B4%EC%9D%98-%EC%B0%A8%EC%9D%B4%EC%A0%90</guid>
            <pubDate>Tue, 30 Apr 2024 10:48:44 GMT</pubDate>
            <description><![CDATA[<h1 id="java--mutable-객체와-immutable-객체의-차이점">[Java] : Mutable 객체와 Immutable 객체의 차이점</h1>
<hr>
<p>자바 객체는 기본적으로 힙 영역에 할당되고 스택 영역에 참조값을 갖는 참조 타입 변수를 통해 데이터에 접근한다. 이러한 객체는 Mutable(가변) 객체와 Immutable(불변) 객체로 나눌 수 있다.</p>
<h2 id="mutable가변-객체">Mutable(가변) 객체</h2>
<p>Mutable(가변) 객체는 힙 영역에 생성된 객체를 변경할 수 있다. </p>
<p>List, ArrayList, HashMap, StringBuilder, StringBuffer 등 우리가 사용하는 대부분의 객체는 가변 객체이다. </p>
<p>그리고 멀티스레드 환경에서 가변 객체를 사용하기 위해서는 동기화 처리가 필요하다.</p>
<hr>
<h2 id="immutable불변-객체">Immutable(불변) 객체</h2>
<p>Immutable(불변) 객체는 데이터 변경이 불가능한 객체를 말한다.</p>
<p>불변 객체는 객체의 데이터 수정이 아예 불가능 한 것이 아니라, 힙 영역에 저장된 값을 수정할 수 없는 것이다.</p>
<p>불변 객체의 종류로는 String, Boolean, Integer, Float, Long, Double 등이 있다.</p>
<pre><code class="language-java">Integer num = 1;

num = 3;</code></pre>
<p>이렇게 불변 객체의 수정이 가능한 이유는 위에서 말했듯이 힙 영역에 저장된 값이 수정된 게 아니라, 힙 영역에 새로운 객체가 생성되었기 때문이다.</p>
<p><img src="https://velog.velcdn.com/images/gdh_/post/db11fa22-0821-4ad5-a012-01ff2e3b477a/image.png" alt=""></p>
<h3 id="불변-객체를-만드는-방법">불변 객체를 만드는 방법</h3>
<p>자바에서는 불변성을 확보할 수 있도록 final 키워드를 제공한다. </p>
<p>자바에서 변수들은 기본적으로 가변적인데, final 키워드를 사용해 참조값을 변경할 수 없도록 해 불변성을 확보할 수 있다.</p>
<pre><code class="language-java">final Integer num = 1;

num = 3;</code></pre>
<p><img src="https://velog.velcdn.com/images/gdh_/post/3e60fdd9-a783-42e2-8ac0-3d95014b8a85/image.png" alt=""></p>
<h3 id="불변-객체의-장단점">불변 객체의 장단점</h3>
<ul>
<li>장점
Thread-safe 하여 멀티스레드 프로그래밍에 유용하며, 동기화를 고려할 필요가 없다.</li>
</ul>
<p>멀티스레드 환경에서의 동기화 문제 발생 이유는 공유 자원에 대한 쓰기 때문이다. 하지만 만약 공유 자원이 불변이라면 항상 동일한 값을 반환하기 때문에 동기화를 고려할 필요가 없다.</p>
<p>따라서 이는 안정성을 보장하고, 동기화를 하지 않음으로 인해 성능상의 이점도 가질 수 있다.</p>
<ul>
<li>단점
객체의 값이 할당될 때마다 새로운 객체가 필요하므로 메모리 누수와 성능저하가 발생할 수 있다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Java] : 자바의 동시성 이슈(공유자원 접근)]]></title>
            <link>https://velog.io/@gdh_/Java-%EC%9E%90%EB%B0%94%EC%9D%98-%EB%8F%99%EC%8B%9C%EC%84%B1-%EC%9D%B4%EC%8A%88%EA%B3%B5%EC%9C%A0%EC%9E%90%EC%9B%90-%EC%A0%91%EA%B7%BC</link>
            <guid>https://velog.io/@gdh_/Java-%EC%9E%90%EB%B0%94%EC%9D%98-%EB%8F%99%EC%8B%9C%EC%84%B1-%EC%9D%B4%EC%8A%88%EA%B3%B5%EC%9C%A0%EC%9E%90%EC%9B%90-%EC%A0%91%EA%B7%BC</guid>
            <pubDate>Tue, 23 Apr 2024 10:28:53 GMT</pubDate>
            <description><![CDATA[<h1 id="java--자바의-동시성-이슈공유자원-접근">[Java] : 자바의 동시성 이슈(공유자원 접근)</h1>
<hr>
<h2 id="동시성-이슈">동시성 이슈</h2>
<p>자바는 멀티 스레드를 지원하는 언어이기 때문에 여러 스레드가 자원을 공유하는 경우 문제점이 생길 수 있다. </p>
<p>여러 스레드에서 동시에 자원에 접근하는 경우 Race Condition이 발생하고 서로 자원을 사용하려는 경쟁 상태로 인해 자원을 일관성을 지킬 수 없게 된다.</p>
<pre><code class="language-java">public class Board {
    private int view = 0;

    public void view() {
        view++;
    }

    public int getView() {
        return view;
    }
}</code></pre>
<pre><code class="language-java">public class MyThread implements Runnable{

    Board board;

    public MyThread(Board board) {
        this.board = board;
    }

    @Override
    public void run() {
        for (int i = 0; i &lt; 50; i++) {
            board.view();
        }
    }
}</code></pre>
<pre><code class="language-java">public class Main {
    public static void main(String[] args) throws InterruptedException {
        Board board = new Board();

        for (int i = 0; i &lt; 100; i++) {
            Thread thread = new Thread(new MyThread(board));
            thread.start();
        }

        Thread.sleep(25000);

        System.out.println(board.getView());
    }
}</code></pre>
<p>예를 들어, 100명의 사람이 한 게시물을 50번 조회할 때 개발자의 생각으로는 조회수가 5000이 나와야 하지만 동시성 이슈로 인해 결과가 5000보다 작은 값이 도출되는 것을 볼 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/gdh_/post/19937b1a-3d0b-494d-b262-12af4e4d8ff6/image.png" alt=""></p>
<p>이러한 동시성 문제를 위해 자바는 여러 해결책을 가지고 있다.</p>
<hr>
<h2 id="1-암시적-lock-synchronized-키워드">1. 암시적 Lock (synchronized 키워드)</h2>
<p>synchronized 키워드는 <strong>임계영역(자원에 대한 동시 접근을 막고 독점을 보장해주는 영역)</strong> 을 만들어 공유 자원에 대한 동시 접근을 막아준다.</p>
<p>synchronized 키워드를 사용하는 방식은 2가지로 특정 영역을 임계영역으로 만들거나 메서드 전체를 임계영역으로 설정할 수 있다.</p>
<h3 id="--특정-영역">- 특정 영역</h3>
<pre><code class="language-java">public class Board {
    private Integer view = 0;

    // 특정 영역에 synchronized
    public void view() {
        synchronized (view) {
            view++;
        }
    }

    public int getView() {
        return view;
    }
}</code></pre>
<h3 id="--메서드">- 메서드</h3>
<pre><code class="language-java">public class Board {
    private int view = 0;

    // 메서드에 synchronized
    public synchronized void view() {
        view++;
    }

    public int getView() {
        return view;
    }
}</code></pre>
<h2 id="2-명시적-lock">2. 명시적 Lock</h2>
<p>Lock 인터페이스를 이용해 Lock 객체의 lock() 메서드를 통해 잠그고, unlock()메서드를 사용해 잠금 해제를 할 수 있다.</p>
<pre><code class="language-java">public class Board {
    private int view = 0;
    private Lock lock = new ReentrantLock();

    // 명시적 Lock
    public void view() {
        lock.lock();
        try {
            // 임계 영역
            view++;
        } finally {
            lock.unlock();
        }
    }

    public int getView() {
        return view;
    }
}</code></pre>
<h2 id="3-concurrent-패키지">3. Concurrent 패키지</h2>
<p>자바의 Concurrent 패키지는 동시성 이슈를 해결하기 위한 다양한 클래스와 인터페이스를 제공하기 때문에 이를 사용하면 내부적으로 스레드 간 안전한 데이터 공유를 보장하므로 동시성 이슈를 예방할 수 있다.</p>
<pre><code class="language-java">public class Board {
    private AtomicInteger view = new AtomicInteger(0);

    // Concurrent패키지 Atomic 사용
    public void view() {
        view.incrementAndGet();
    }

    public int getView() {
        return view.get();
    }
}</code></pre>
<p><img src="https://velog.velcdn.com/images/gdh_/post/137f643e-366c-4976-ba1d-091e9c35f38b/image.png" alt=""></p>
<p>이와 같은 방법들을 사용한다면 처음 개발자의 예상과 같이 5000의 결과가 도출되는 것을 볼 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[DB 캐시 (로컬 캐싱, 글로벌 캐싱)]]></title>
            <link>https://velog.io/@gdh_/DB-%EC%BA%90%EC%8B%9C-%EB%A1%9C%EC%BB%AC-%EC%BA%90%EC%8B%B1-%EA%B8%80%EB%A1%9C%EB%B2%8C-%EC%BA%90%EC%8B%B1</link>
            <guid>https://velog.io/@gdh_/DB-%EC%BA%90%EC%8B%9C-%EB%A1%9C%EC%BB%AC-%EC%BA%90%EC%8B%B1-%EA%B8%80%EB%A1%9C%EB%B2%8C-%EC%BA%90%EC%8B%B1</guid>
            <pubDate>Wed, 17 Apr 2024 10:56:30 GMT</pubDate>
            <description><![CDATA[<h1 id="db-캐시-로컬-캐싱-글로벌-캐싱">DB 캐시 (로컬 캐싱, 글로벌 캐싱)</h1>
<hr>
<h2 id="파레토-법칙">파레토 법칙</h2>
<p><img src="https://velog.velcdn.com/images/gdh_/post/007c986a-0b95-47f4-b44c-9b9fe59228b7/image.png" alt=""></p>
<p>80%의 결과는 20%의 원인으로 인해 발생된다는 법칙으로 캐시가 나오게 된 배경에 이 법칙이 존재한다. 서비스에 필요한 20%를 캐싱함으로써 전체적인 효율을 끌어올릴 수 있다.</p>
<h2 id="캐시">캐시</h2>
<p>캐시란, 데이터의 원본보다 더 빠르고 효율적으로 액세스할 수 있는 임시 데이터 저장소이다.</p>
<p>자주 조회되며 데이터의 변경이 적은 데이터는 데이터 갱신으로 인해 DB와 불일치될 확률이 적기때문에 캐시를 적용하기 적합하다. 또한 데이터의 최신화가 실시간으로 이루어지지 않아도 괜찮은 데이터는 캐시와 DB사이 불일치가 길어도 괜찮기 때문에 적합하다.</p>
<p>캐시를 사용함으로써 서버간 불필요한 트래픽을 줄일 수 있으며, 부하가 감소된다.</p>
<h2 id="로컬-캐싱">로컬 캐싱</h2>
<p>로컬 캐싱이란, 로컬 장비 내에서 사용되는 캐시 메모리를 이용하는 방식이다.</p>
<p><img src="https://velog.velcdn.com/images/gdh_/post/22ebac88-ffea-4152-b6df-f776745d389b/image.png" alt=""></p>
<h3 id="장점"><strong>장점</strong></h3>
<ul>
<li>로컬에서 동작하기 때문에 속도가 빠르며, 네트워크 단절, 지연에 자유롭다.</li>
<li>서버와 라이프 사이클을 같이하기 때문에 사용하기 편리하다.</li>
</ul>
<h3 id="단점"><strong>단점</strong></h3>
<ul>
<li>서버가 다운되면 메모리도 휘발성이므로 사라지게 된다.</li>
<li>여러 서버가 존재할 경우 서버간의 메모리 캐시 데이터의 불일치 문제가 생긴다.</li>
<li>서버의 메모리가 부족해질 경우 서버 스펙을 향상시킬 필요가 있다.</li>
</ul>
<h2 id="글로벌-캐싱">글로벌 캐싱</h2>
<p>글로벌 캐싱이란, 여러 서버에서 별도의 서버의 인메모리 DB(ex. Redis)에 저장된 캐시를 이용하는 방식이다.</p>
<p><img src="https://velog.velcdn.com/images/gdh_/post/8afa1683-59c0-4f81-af1c-bc9eb99ea638/image.png" alt=""></p>
<h3 id="장점-1"><strong>장점</strong></h3>
<ul>
<li>여러 서버간 캐시를 공유하므로 서버 간 데이터 공유가 쉽다.</li>
<li>새로운 서버 생성시 그 로컬 캐시를 새로 채울 필요가 없으므로 확장성이 좋다.</li>
</ul>
<h3 id="단점-1"><strong>단점</strong></h3>
<ul>
<li>네트워크 트래픽으로 인해 속도가 로컬에 비해 상대적으로 느리다.</li>
<li>별도의 캐시 서버를 두어야 하므로 별도의 인프라 관리 비용이 존재한다.</li>
</ul>
<hr>
<h2 id="캐싱-전략">캐싱 전략</h2>
<p>일반적으로 캐시는 DB보다 훨씬 빠르게 데이터를 응답할 수 있다. </p>
<p>하지만 캐시의 용량이 DB보다 적기 때문에, 데이터를 모두 캐시에 저장해버린다면 용량 부족 현상이 일어난다. </p>
<p>따라서 어떤 데이터를 캐시에 저장할지, 얼만큼 데이터를 캐시에 저장할지, 얼마나 오래된 데이터를 캐시에서 제거할지에 대한 전략이 중요하다.</p>
<ul>
<li>cache hit : 캐시에 데이터가 있을 경우 바로 가져온다.</li>
<li>cache miss : 캐시에 데이터가 없을 경우 DB에서 가져온다.
<img src="https://velog.velcdn.com/images/gdh_/post/dd1b117e-7664-4c04-9416-fe33edb75d68/image.png" alt=""></li>
</ul>
<p>캐시를 이용할 때 데이터 불일치 문제를 고려할 필요가 있다.</p>
<p>한 데이터가 캐시와 DB 두 곳에서 같은 데이터임에도 그 값이 다른 경우를 말한다.</p>
<p>따라서 적절한 캐시 읽기 전략과 쓰기 전략을 통해 데이터 불일치 문제를 극복해야 한다.</p>
<h2 id="캐시-읽기-전략-read-cache-strategy">캐시 읽기 전략 (Read Cache Strategy)</h2>
<h3 id="look-asidecache-aside-패턴">Look Aside(Cache Aside) 패턴</h3>
<p>Look Aside(Cache Aside) 패턴은 가장 기본적인 캐시 전략으로, 데이터를 찾을 때 우선 캐시에 저장된 데이터가 있는지 우선적으로 확인하고 데이터가 없다면 DB에서 조회한다.</p>
<p><img src="https://velog.velcdn.com/images/gdh_/post/196f9746-1ec7-4c4f-b8ff-226a7f7004bd/image.png" alt=""></p>
<ol>
<li>캐시에서 데이터가 있는지 확인한다.</li>
<li>캐시에서 없을 경우 DB에서 데이터를 조회한다.</li>
<li>DB에서 조회해온 데이터를 캐시에 업데이트한다.</li>
</ol>
<p>이 방식은 초기 조회 시 무조건 DB에서 조회해야 하므로 단건 호출 빈도가 높은 서비스보다는 반복적으로 동일 쿼리를 수행하는 서비스에 적합하다. (초기에 DB에서 캐시로 data를 미리 넣어주는 <strong>Cache Warming</strong>을 통해 초기 성능 저하를 어느정도 방지할수도 있다.)</p>
<h2 id="read-through-패턴">Read Through 패턴</h2>
<p>Read Through 패턴은 캐시에서만 데이터를 읽어오는 전략으로, Look Aside와 비슷하지만 캐시에 데이터 동기화를 위임해, 캐시에 데이터를 업데이트하는 주체가 서버냐 DB냐에 차이가 있다. 그리고 Look Aside와 달리 항상 캐시와 DB간 데이터 동기화가 항상 이루어져 데이터 불일치 문제에서 벗어날 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/gdh_/post/52f4ecbd-6413-40ca-a4d0-6776017200b5/image.png" alt=""></p>
<ol>
<li>캐시에서 데이터가 있는지 확인한다.</li>
<li>캐시에서 없을 경우 캐시와 DB간 데이터를 자체 업데이트 한다.</li>
<li>캐시에서 데이터를 가져온다.</li>
</ol>
<p>이 방식 또한 Cache Warming을 수행하는 것이 효율적이나 데이터를 조회하는 데 있어 상대적으로 속도가 느리다.</p>
<h2 id="캐시-쓰기-전략-write-cache-strategy">캐시 쓰기 전략 (Write Cache Strategy)</h2>
<h2 id="write-back-write-behind-패턴">Write Back (Write Behind) 패턴</h2>
<p>Write Back (Write Behind) 패턴은 캐시와 DB를 비동기로 하기 때문에 동기화 과정이 생략되며, 데이터를 저장할 때 DB에 바로 쿼리하지 않고, 캐시에 모아서 일정 주기로 DB에 반영한다.</p>
<p><img src="https://velog.velcdn.com/images/gdh_/post/87b89fa1-70a3-4701-a39c-6e2b29a7b536/image.png" alt=""></p>
<ol>
<li>모든 데이터를 캐시에 저장한다.</li>
<li>일정 시간 주기로 DB에 저장한다.</li>
</ol>
<p>이 방식은 캐시에 데이터를 모았다 한번에 DB에 저장하기 때문에 DB 쓰기 비용과 부하를 줄일 수 있지만, DB에 데이터를 쓰기 전에 캐시에 장애가 발생하면 데이터 유실이 발생 가능하다.</p>
<h2 id="write-through-패턴">Write Through 패턴</h2>
<p>Write Through 패턴은 DB와 캐시에 동시에 데이터를 저장하는 전략으로, 데이터를 저장할 때 먼저 캐시에 저장하고 바로 DB에 저장한다. Read Through와 같이 DB 동기화 작업을 캐시에 위임한다.</p>
<p><img src="https://velog.velcdn.com/images/gdh_/post/87b89fa1-70a3-4701-a39c-6e2b29a7b536/image.png" alt=""></p>
<ol>
<li>DB에 저장할 데이터가 존재하면 우선 캐시에 저장한다.</li>
<li>그 다음 바로 DB에 데이터를 저장한다.</li>
</ol>
<p>이 방식은 DB와 캐시가 항상 동기화 되어있어, 데이터의 일관성을 유지할 수 있다. 하지만 저장에 2단계 과정을 거치므로 상대적으로 속도가 느리다.</p>
<h2 id="write-around-패턴">Write Around 패턴</h2>
<p>Write Around 패턴은 모든 데이터를 DB에 저장하는 전략으로 캐시를 갱신하지 않는다.</p>
<p><img src="https://velog.velcdn.com/images/gdh_/post/00d9020d-458d-4832-908c-a017b149d615/image.png" alt=""></p>
<ol>
<li>모든 데이터는 DB에 저장한다.</li>
</ol>
<p>이 방식은 데이터를 DB에만 저장하므로 속도가 빠르지만, 캐시와 DB간 데이터 불일치 문제가 발생 가능하다.</p>
<hr>
<h2 id="캐시-읽기--쓰기-전략-조합">캐시 읽기 + 쓰기 전략 조합</h2>
<h2 id="look-aside--write-around-조합">Look Aside + Write Around 조합</h2>
<p>가장 일반적으로 자주 쓰이는 조합이다.</p>
<p><img src="https://velog.velcdn.com/images/gdh_/post/3238d57c-3806-42cd-9ca1-64a79c394e4a/image.png" alt=""></p>
<h2 id="read-through--write-around-조합">Read Through + Write Around 조합</h2>
<p>항상 DB에 쓰고, 캐시에서 읽을 때 항상 DB에서 먼저 읽어오므로 데이터 불일치 문제에 대해 완벽한 안전 장치를 구성할 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/gdh_/post/f89b4466-3735-459b-adf8-9b3bf14a96a2/image.png" alt=""></p>
<h2 id="read-through--write-through-조합">Read Through + Write Through 조합</h2>
<p>데이터를 쓸 때 항상 캐시에 먼저 쓰므로, 읽어올 때 최신 캐시 데이터가 보장된다.</p>
<p>또한 데이터를 쓸 때 항상 캐시에서 DB에 보내므로 데이터의 정합성이 보장된다.</p>
<p><img src="https://velog.velcdn.com/images/gdh_/post/7ccfd61c-dcbe-4d4d-aa50-99477ce1ce92/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Java] : 직렬화와 역직렬화]]></title>
            <link>https://velog.io/@gdh_/Java-%EC%A7%81%EB%A0%AC%ED%99%94%EC%99%80-%EC%97%AD%EC%A7%81%EB%A0%AC%ED%99%94</link>
            <guid>https://velog.io/@gdh_/Java-%EC%A7%81%EB%A0%AC%ED%99%94%EC%99%80-%EC%97%AD%EC%A7%81%EB%A0%AC%ED%99%94</guid>
            <pubDate>Tue, 09 Apr 2024 10:36:26 GMT</pubDate>
            <description><![CDATA[<h1 id="java--직렬화와-역직렬화">[Java] : 직렬화와 역직렬화</h1>
<hr>
<p><img src="https://velog.velcdn.com/images/gdh_/post/348388e1-b635-41b5-94df-55111cdd9d72/image.png" alt=""></p>
<h2 id="직렬화">직렬화</h2>
<p>직렬화(Serialize)란 자바 언어에서 사용되는 Object나 Data를 다른 컴퓨터의 자바 시스템에서도 사용할 수 있도록 바이트 스트림 형태로 연속적인 데이터로 변환하는 포맷 변환 기술을 말한다.</p>
<h2 id="역직렬화">역직렬화</h2>
<p>역직렬화(Deserialize)란 바이트로 변환된 데이터를 원래대로 자바 시스템의 Object나 Data로 변환하는 기술이다.</p>
<blockquote>
<p><strong>바이트 스트림이란?</strong>
스트림은 클라이언트나 서버 간에 입출력하기 위한 데이터가 흐르는 통로를 말한다. 
자바는 스트림의 기본 단위를 바이트로 두고 있기 때문에, 네트워크, 데이터베이스로 전송하기 위해 최소 단위인 바이트 스트림으로 변환하여 처리한다.</p>
</blockquote>
<hr>
<h2 id="직렬화-vs-json">직렬화 vs JSON</h2>
<p>직렬화는 외부 파일이나 네트워크를 통해 클라이언트 간 객체 데이터를 주고 받을 때 사용된다.</p>
<p>하지만 JSON이라는 데이터 포맷이 존재하는데 굳이 직렬화가 필요할까?</p>
<p>실제로 JSON은 데이터를 교환할 때 범용적으로 사용되고, 직렬화는 오직 자바 프로그램에서만 사용 가능하지만, JSON 형태 데이터는 파이썬, 자바스크립트 등 다른 시스템에서도 사용 가능하다.</p>
<h2 id="직렬화-장점">직렬화 장점</h2>
<ul>
<li><p>직렬화는 자바의 고유 기술이므로 자바 시스템 개발에 최적화 되어 있다.</p>
</li>
<li><p>자바의 수많은 레퍼런스 타입에 대해 제약 없이 외부에 내보낼 수 있다.</p>
</li>
</ul>
<p>기본형 타입이나 배열 같은 타입은 다른 프로그래밍 언어도 공통적으로 사용하는 타입이기 때문에, JSON으로도 충분히 상호 이용 가능하다.</p>
<p>하지만 자바의 컬렉션들이나 클래스, 인터페이스 타입들은 단순 파일 포맷으로는 타입 개수의 한계가 있다. 그래서 이들을 외부로 보내기 위해서는 별도의 파싱 작업이 필요하다.</p>
<p>그에 반해, 직렬화를 이용하면 다른 시스템에서는 사용 못하더라도 별도의 파싱 작업없이 외부로 보낼 수 있다. 그리고 역직렬화를 통해 읽어들이면 데이터 타입이 자동으로 맞춰지기 때문에 클래스 기능을 바로 다시 이용 가능하다.</p>
<h2 id="직렬화-단점">직렬화 단점</h2>
<p>Serializable 인터페이스를 implemets하면 간단히 직렬화가 가능하지만 그에 따른 대가는 매우 비싸다.</p>
<ul>
<li><p>직렬화는 객체에 저장된 데이터값 뿐만 아니라 타입 정보, 클래스 메타 정보를 가지고 있으므로 용량을 많이 차지한다. 따라서 DB 등 외부에 저장할 때, 장기간 저장하는 정보는 직렬화를 지양해야 한다.</p>
</li>
<li><p>역직렬화 과정에서 호출되어 잠재적으로 위험한 동작을 수행하는 메소드를 가젯(gadget)이라 부르는데, 남이 만든 것을 역직렬화하는 과정에서 나도 모르게 공격당할 위험이 있다.</p>
</li>
<li><p>클래스가 Serializable을 구현하게 되면 직렬화된 바이트 스트림 인코딩도 하나의 공개 api가 되므로 그 직렬화 형태도 영원히 지원해야한다. 따라서 Serializable을 구현한 순간부터 객체의 유지보수는 직렬화에 묶이게 된다.</p>
</li>
</ul>
<hr>
<h2 id="직렬화--역직렬화-방법">직렬화 &amp; 역직렬화 방법</h2>
<p>객체를 직렬화 하기 위해서는 Serializable 인터페이스를 implements 해야한다. 그렇지 않으면 NotSerializableException 예외가 발생한다.</p>
<p>Serializable 인터페이스는 아무런 내용도 없는 마커 인터페이스로, 직렬화를 하겠다는 의도를 표시하는 용도이다.</p>
<pre><code class="language-java">public class Food implements Serializable {
    String name;
    int price;
    int cal;

    public Food(String name, int price, int cal) {
        this.name = name;
        this.price = price;
        this.cal = cal;
    }

    @Override
    public String toString() {
        return &quot;Food{&quot; +
               &quot;name=&quot; + name +
               &quot;, price=&quot; + price +
               &quot;, cal=&quot; + cal +
               &quot;}&quot;;
    }
}</code></pre>
<pre><code class="language-java">public static void main(String[] args) {
    // 직렬화
    Food food = new Food(&quot;pasta&quot;, 10000, 500);

    String fileName = &quot;Food.ser&quot;;

    try(FileOutputStream fos = new FileOutputStream(fileName); ObjectOutputStream oos = new ObjectOutputStream(fos)) {
        oos.writeObject(food);
    } catch (IOException e) {
        e.printStackTrace();
    }
}</code></pre>
<p>Food.ser 파일을 보면 이렇게 직렬화가 된 형태를 볼 수 있다.
<img src="https://velog.velcdn.com/images/gdh_/post/a587ee43-50cb-4fe4-8bdb-44e67cd44f25/image.png" alt=""></p>
<pre><code class="language-java">public static void main(String[] args) {
    // 역직렬화
    String fileName = &quot;Food.ser&quot;;

    try(FileInputStream fis = new FileInputStream(fileName); ObjectInputStream ois = new ObjectInputStream(fis)) {
        Food food = (Food) ois.readObject();
        System.out.println(food);
    } catch (IOException | ClassNotFoundException e) {
        e.printStackTrace();
    }
}</code></pre>
<p>역직렬화의 결과로 아래와 같이 출력된다.
<img src="https://velog.velcdn.com/images/gdh_/post/d6f90ab8-063e-441a-9219-98e5e592c0ed/image.png" alt=""></p>
<h2 id="직렬화-리스트-관리">직렬화 리스트 관리</h2>
<p>여러 객체를 직렬화하고 이를 역직렬화한다면 순서가 중요하다. 역직렬화 할 때는 직렬화 할 때의 순서와 일치해야 된다. 따라서 직렬화할 객체가 많다면 ArrayList와 같은 컬렉션에 저장해서 순서를 고려할 필요없이 관리하는 것이 좋다.</p>
<pre><code class="language-java">public static void main(String[] args) {
    // 리스트 직렬화
    Food pasta = new Food(&quot;pasta&quot;, 10000, 500);
    Food pizza = new Food(&quot;pizza&quot;, 15000, 1000);
    Food chicken = new Food(&quot;chkicken&quot;, 20000, 1000);

    String fileName = &quot;Food.ser&quot;;

    List&lt;Food&gt; foods = new ArrayList&lt;&gt;();
    foods.add(pasta);
    foods.add(pizza);
    foods.add(chicken);

    try(FileOutputStream fos = new FileOutputStream(fileName); ObjectOutputStream oos = new ObjectOutputStream(fos)) {
        oos.writeObject(foods);
    } catch (IOException e) {
        e.printStackTrace();
    }
}</code></pre>
<pre><code class="language-java">public static void main(String[] args) {
    // 리스트 역직렬화
    String fileName = &quot;Food.ser&quot;;

    try(FileInputStream fis = new FileInputStream(fileName); ObjectInputStream ois = new ObjectInputStream(fis)) {
        List&lt;Food&gt; foods = (List&lt;Food&gt;) ois.readObject();
        System.out.println(foods);
    } catch (IOException | ClassNotFoundException e) {
        e.printStackTrace();
    }
}</code></pre>
<p><img src="https://velog.velcdn.com/images/gdh_/post/4789a9f2-c191-4887-ab35-7cb92de9884e/image.png" alt=""></p>
<hr>
<h2 id="직렬화-요소-제외">직렬화 요소 제외</h2>
<p>transient 키워드를 사용해 직렬화 대상에서 변수가 제외되도록 할 수 있다. transient가 붙은 인스턴스 변수 값은 그 타입의 기본값으로 직렬화된다.</p>
<ul>
<li>Primitive 타입 : 각 타입의 디폴트 값</li>
<li>Reference 타입 : null</li>
</ul>
<pre><code class="language-java">public class Food implements Serializable {
    String name;
    int price;
    transient int cal;
}</code></pre>
<h2 id="자바-직렬화-버전-관리">자바 직렬화 버전 관리</h2>
<p>Serializable 인터페이스를 구현하는 모든 직렬화된 클래스는 serialVersionUID 라는 고유 식별번호를 부여받는다. 이 식별 ID는 클래스를 직렬화, 역직렬화하는 과정에서 동일한 특성을 갖는 지 확인하는데 사용된다.</p>
<pre><code class="language-java">public class Food implements Serializable {
    String name;
    int price;
    int cal;
}

public class Food implements Serializable {
    String name;
    int price;
    int cal;

    int sale; // 변경사항
}</code></pre>
<p>위와 같이 클래스에 변경사항이 생겼을 경우 역직렬화시 아래와 같은 예외가 나타나게 된다.</p>
<p><img src="https://velog.velcdn.com/images/gdh_/post/e75d14fb-e804-40c4-90f9-191bf465ecbd/image.png" alt=""></p>
<p>따라서 직렬화 클래스는 serialVersionUID를 직접 명시해 버전을 수동으로 관리하는 것을 권장한다. SUID를 직접 명시하면 클래스의 내용이 변경되어도 버전이 시스템이 자동 생성된 값으로 변경되지 않기 때문이다.</p>
<pre><code class="language-java">public class Food implements Serializable {
    private static final long serialVersionUID = 123L;

    String name;
    int price;
    int cal;
}</code></pre>
<p>이렇게 수동으로 버전 관리를 하더라도 필드 변수의 타입 변경시에는 예외가 나타나기 때문에 그 점을 주의해야 한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Java] : 강한 결합과 느슨한 결합]]></title>
            <link>https://velog.io/@gdh_/Java-%EA%B0%95%ED%95%9C-%EA%B2%B0%ED%95%A9%EA%B3%BC-%EB%8A%90%EC%8A%A8%ED%95%9C-%EA%B2%B0%ED%95%A9</link>
            <guid>https://velog.io/@gdh_/Java-%EA%B0%95%ED%95%9C-%EA%B2%B0%ED%95%A9%EA%B3%BC-%EB%8A%90%EC%8A%A8%ED%95%9C-%EA%B2%B0%ED%95%A9</guid>
            <pubDate>Thu, 04 Apr 2024 10:28:40 GMT</pubDate>
            <description><![CDATA[<h1 id="java--강한-결합과-느슨한-결합">[Java] : 강한 결합과 느슨한 결합</h1>
<hr>
<h2 id="모듈화">모듈화</h2>
<p>모듈화란 소프트웨어를 기능별로 나누는 것을 의미하며, 각 기능별로 나누어진 결과를 모듈이라고 한다.</p>
<p>좋은 모듈화는 목적에 맞는 기능만으로 모듈을 나누어 다른 모듈과 적게 연관되는 것(독립성이 강한 모듈)을 의미한다.</p>
<p>모듈의 독립성은 모듈의 결합도와 응집도로 측정하며, 낮은 결합도와 높은 응집도일수록 모듈의 독립성이 높다고 한다.</p>
<ul>
<li><strong>결합도(Coupling)</strong>: 서로 다른 모듈 간 상호 의존 정도</li>
<li><strong>응집도(Cohesion)</strong>: 모듈 내부 구성 요소 간 서로 관련되어 있는 정도</li>
</ul>
<hr>
<h2 id="강한-결합">강한 결합</h2>
<p>어떠한 객체가 다른 객체에 강한 의존성을 가지고 있는 것</p>
<pre><code class="language-java">class Person {
    private Pasta pasta;

    public Person() {
        this.pasta = new Pasta();
    }

    public void startEat() {
        pasta.eat();
    }
}</code></pre>
<pre><code class="language-java">class Pasta {
    public void eat() {
        System.out.println(&quot;파스타를 먹습니다.&quot;);
    }
}</code></pre>
<p>Person 클래스의 멤버 변수로 Pasta 클래스가 존재한다. </p>
<p>이 코드의 단점은 Pasta 클래스가 없으면 Person 클래스를 정의 할 수 없다. 
그리고 Pasta 클래스를 다른 종류의 클래스로 바꾸게 되면 Person 클래스의 코드 대부분을 변경해야 한다. 유지 보수성이 낮아진다.</p>
<p>즉, Person 클래스가 Pasta 클래스에 의존하게 되고, 의존성이 존재하게 된다.</p>
<h2 id="약한-결합">약한 결합</h2>
<p>객체 간의 강한 결합을 약화시켜 약하게 만드는 방법은 인터페이스이다.</p>
<pre><code class="language-java">interface Food {
    void eat();
}</code></pre>
<pre><code class="language-java">class Person {
    private Food food;

    public Person(Food food) {
        this.food = food;
    }

    public void startEat() {
        food.eat();
    }
}</code></pre>
<pre><code class="language-java">class Pasta implements Food {
    @Override
    public void eat() {
        System.out.println(&quot;파스타를 먹습니다.&quot;);
    }
}</code></pre>
<pre><code class="language-java">class Pizza implements Food {
    @Override
    public void eat() {
        System.out.println(&quot;피자를 먹습니다.&quot;);
    }
}</code></pre>
<p>인터페이스를 만들어 추상 메소드를 선언하고 Pasta, Pizza 클래스에서 이를 구현한다. </p>
<p>그러면 Pasta 객체와 Pizza 객체는 Food 타입에 대입될 수 있으므로 Person 클래스에서 멤버 변수 타입을 Food로 할 수 있다.</p>
<p>따라서 이전의 강한 결합의 코드와 달리 Person 내부 코드의 변경 없이 생성자를 통해 객체로 받아 멤버 변수에 대입하면 오브젝트를 변경할 수 있다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Java] : try-with-resource]]></title>
            <link>https://velog.io/@gdh_/Java-try-with-resource</link>
            <guid>https://velog.io/@gdh_/Java-try-with-resource</guid>
            <pubDate>Mon, 01 Apr 2024 11:20:39 GMT</pubDate>
            <description><![CDATA[<h1 id="java--try-with-resource">[Java] : try-with-resource</h1>
<hr>
<h2 id="자바-resource의-예외-처리">자바 Resource의 예외 처리</h2>
<p>resource란 외부의 데이터(DB, Network, File)을 말한다. 이러한 resource들은 자바 내부에 위치한 요소들이 아니므로, 외부 데이터에 접근하려 할 때 예외가 발생할 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/gdh_/post/b2455965-e851-4649-b049-6aa5273d75a0/image.png" alt=""></p>
<hr>
<h2 id="자바7-이전의-try-catch-finally">자바7 이전의 try-catch-finally</h2>
<p>사용 후에 반납해줘야 하는 자원들은 Closable 인터페이스를 구현하고 있으며, 사용 후에 close() 메소드를 호출해주어야 한다.</p>
<p>자바7 이전에는 close()에서 발생하는 예외 또한 처리하기 위해 아래처럼 코드가 복잡해지는 문제가 존재했다.</p>
<pre><code class="language-java">public static void main(String[] args) throws IOException {
    FileInputStream is = null;
    FileOutputStream os = null;

    try {
        is = new FileInputStream(&quot;input.txt&quot;);
        os = new FileOutputStream(&quot;output.txt&quot;);

        int data = -1;
        while ((data = is.read()) != -1) {
            System.out.print((char) data);
            os.write(data);
        }
    } catch (IOException e) {
        throw new RuntimeException(e);
    } finally {
        // close()에 대한 예외처리도 필요
        if (is != null) is.close();

        try {
            os.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}</code></pre>
<p>자원 반납에 의한 코드 복잡성 문제 뿐만 아니라 try-catch문은 여러 단점이 존재한다. 에러로 자원을 반납하지 못하는 경우가 발생할 수 있고, 에러 스택 트레이스가 누락되어 디버깅이 어려울 수 있다.</p>
<h2 id="자바7-부터의-try-with-resources">자바7 부터의 try-with-resources</h2>
<p>자바는 이러한 문제점을 해결하고자 자바7부터 자원을 자동으로 반납해주는 try-with-resources 문법을 추가하였다. 자바는 AutoCloseable 인터페이스를 구현하고 있는 자원에 대해 try-with-resources를 적용 가능하도록 하였고, 이를 사용함으로써 코드가 유연해지고, 누락되는 에러없이 모든 에러를 잡을 수 있게 되었다.</p>
<p><img src="https://velog.velcdn.com/images/gdh_/post/6374b4b2-39c3-4efd-b0a8-cc690f5d20ab/image.png" alt=""></p>
<p>try 블록에 괄호()를 추가해 자원을 할당하는 코드를 명시하면, 해당 try 블록이 끝나자마자 자동으로 할당된 자원을 해제한다.</p>
<pre><code class="language-java">public static void main(String[] args) throws IOException {
    try (FileInputStream is = new FileInputStream(&quot;input.txt&quot;);
         FileOutputStream os = new FileOutputStream(&quot;output.txt&quot;)) {
        int data = -1;
        while ((data = is.read()) != -1) {
            System.out.print((char) data);
            os.write(data);
        }
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
}</code></pre>
<hr>
<h2 id="자원을-반납하지-못하는-경우">자원을 반납하지 못하는 경우</h2>
<p>try-catch-finally 문에서 여러 자원을 사용하고 반납하는 경우 자원의 반납이 이루어지지 않을 수 있다.</p>
<pre><code class="language-java">public class MyResource implements AutoCloseable{ 
    @Override 
    public void close() throws RuntimeException { 
        System.out.println(&quot;close&quot;); 
        throw new RuntimeException(); 
    } 

    public void hello() { 
        System.out.println(&quot;hello&quot;); 
    } 
}</code></pre>
<pre><code class="language-java">public static void try1() {
    MyResource myResource1 = null;
    MyResource myResource2 = null;

    try {
        myResource1 = new MyResource();
        myResource2 = new MyResource();
        myResource1.hello();
        myResource2.hello();
    } finally {
        if (myResource1 != null) myResource1.close();

        if (myResource2 != null) myResource2.close();
    }
}

출력
hello
hello
close</code></pre>
<p>이 문제를 해결하기 위해서는 if문으로 null처리한 close() 메서드를 try-catch문을 통해 catch에서 예외처리를 해야하지만 이는 코드를 복잡하게 만든다.</p>
<p>하지만 try-with-resources문은 별도의 처리없이 문제 해결이 가능하다.</p>
<pre><code class="language-java">public static void try2() {
    try (MyResource myResource1 = new MyResource();
         MyResource myResource2 = new MyResource()) {
        myResource1.hello();
        myResource2.hello();
    }
}

출력
hello
hello
close
close</code></pre>
<p>Java 파일이 Class 파일로 컴파일 될 때 try-with-resources에서 누락없이 모든 경우를 try-catch-finally로 변환해주기 때문에 간단하게 문제 해결이 가능하다. 위에서 직접 구현해주어야 했던 부분을 컴파일러가 처리해준 것이다.</p>
<hr>
<h2 id="에러-스택-트레이스-누락-경우">에러 스택 트레이스 누락 경우</h2>
<p>try-catch-finally 문을 사용하면 에러가 발생해도 스택 트레이스에 누락되는 경우가 발생할 수 있다.</p>
<pre><code class="language-java">public class MyResource implements AutoCloseable{ 
    @Override 
    public void close() throws RuntimeException { 
        System.out.println(&quot;close&quot;); 
        throw new RuntimeException(); 
    } 

    public void hello() { 
        System.out.println(&quot;hello&quot;); 
        throw new RuntimeException(); 
    } 
}</code></pre>
<pre><code class="language-java">public static void try3() {
    MyResource myResource = null;

    try {
        myResource = new MyResource();
        myResource.hello();
    } finally {
        if (myResource != null) myResource.close();
    }
}</code></pre>
<p><img src="https://velog.velcdn.com/images/gdh_/post/6762b749-7596-43b2-86ca-e2adc35ec6f0/image.png" alt=""></p>
<p>try3()를 호출했을 때 예상과는 다르게 hello()에서의 예외는 누락된 채 close()의 예외만 잡히는 것을 볼 수 있다.</p>
<p>하지만 try-with-resources문을 사용하면 누락되는 에러없이 모든 에러가 스택 트레이스에 잡히는 것을 볼 수 있다.</p>
<pre><code class="language-java">public static void try4() {
    try (MyResource myResource1 = new MyResource()) {
        myResource1.hello();
    }
}</code></pre>
<p><img src="https://velog.velcdn.com/images/gdh_/post/9da6be33-0efe-4ae8-96e8-6577eeb73a51/image.png" alt=""></p>
<p>따라서 자원을 반납하는 경우가 생긴다면 코드의 복잡도, 자원 반납 누락, 스택 트레이스 에러 누락 등의 문제가 없는 try-with-resources문을 사용하는 것이 적합하다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Java] : Error, Checked Exception, Unchecked Exception]]></title>
            <link>https://velog.io/@gdh_/Java-Error-Checked-Exception-Unchecked-Exception</link>
            <guid>https://velog.io/@gdh_/Java-Error-Checked-Exception-Unchecked-Exception</guid>
            <pubDate>Wed, 20 Mar 2024 11:52:42 GMT</pubDate>
            <description><![CDATA[<h1 id="java--error-checked-exception-unchecked-exception">[Java] : Error, Checked Exception, Unchecked Exception</h1>
<hr>
<p><img src="https://velog.velcdn.com/images/gdh_/post/116a574b-d218-42ff-b206-15f747780edb/image.png" alt=""></p>
<h2 id="error">Error</h2>
<p>에러란 시스템에 비정상적인 상황이 생겼을 때 발생하며 메모리 부족(OutOfMemoryError)이나 스택오버플로우(StackOverflowError) 같은 에러가 존재한다. 개발자가 미리 예측할 수 없는 문제이다.</p>
<h2 id="exception">Exception</h2>
<p>예외는 사용자의 잘못된 조작이나 개발자의 잘못된 코딩으로 인해 발생하는 프로그램 오류이다. 예외는 예외처리를 통해 프로그램이 종료되지 않고 정상적으로 작동되게 만들 수 있다.</p>
<h3 id="checked-exception">Checked Exception</h3>
<p>RuntimeException의 하위 클래스가 아니면서 Exception 클래스의 하위 클래스로 파일이나 클래스명을 잘못 입력했을 때의 FileNotFoundException, ClassNotFoundException 등이 있다.</p>
<ul>
<li>명시적으로 예외 처리를 강제하며, 예외 처리 하지 않으면 컴파일 시 IDE에 에러가 발생한다.</li>
<li>try/catch, throw 등을 사용해 예외 처리한다.</li>
</ul>
<h3 id="unchecked-exception">Unchecked Exception</h3>
<p>RuntimeException의 하위 클래스를 의미하며, 실행 중에 발생할 수 있는 예외를 의미한다. 배열의 범위를 벗어나거나, 값이 null인 참조변수를 참조할 때 ArrayIndexOutOfBoundsException, NullPointerException 등이 있다.</p>
<ul>
<li>명시적으로 예외 처리를 강제하지 않으며, 예외 처리 하지 않아도 IDE에 에러가 발생하지 않는다.</li>
</ul>
<br>

<table>
<thead>
<tr>
<th align="center"></th>
<th align="center">Error</th>
<th align="center">Checked Exception</th>
<th align="center">Unchecked Exception</th>
</tr>
</thead>
<tbody><tr>
<td align="center">확인 시점</td>
<td align="center">실행시</td>
<td align="center">컴파일시</td>
<td align="center">실행시</td>
</tr>
<tr>
<td align="center">발생 이유</td>
<td align="center">시스템의 비정상적인 상황 발생</td>
<td align="center">주로 외부의 영향으로 발생</td>
<td align="center">주로 개발자의 실수에 의해 발생</td>
</tr>
<tr>
<td align="center">처리 여부</td>
<td align="center">불가능</td>
<td align="center">명시적 처리 강제</td>
<td align="center">명시적 처리 강제 X</td>
</tr>
<tr>
<td align="center">기본 트랜젝션 여부</td>
<td align="center">Rollback</td>
<td align="center">Non-Rollback</td>
<td align="center">Rollback</td>
</tr>
</tbody></table>
<hr>
<h2 id="spring-transactional과-exception">Spring @Transactional과 Exception</h2>
<p>Spring @Transactional은 기본적으로 Checked Exception을 Non-Rollback한다. 하지만 Checked Exception이라고 무조건 Non-Rollback이 아니라 Rollback을 발생시킬 수 있다.</p>
<h3 id="checked-exception-rollback">Checked Exception Rollback</h3>
<ul>
<li>@Transactional의 rollbackFor 옵션</li>
</ul>
<pre><code class="language-java">// FileNotFoundException은 rollback
@Transactional(rollbackFor = FileNotFoundException.class)

// 여러개의 예외를 rollback 한다.
@Transactional(rollbackFor = {FileNotFoundException.class, ClassNotFoundException.class})</code></pre>
<ul>
<li>throw를 사용해 UnChecked Exception으로 처리</li>
</ul>
<pre><code class="language-java">try{
    // 로직
} catch(FileNotFoundException e) {
    throw new RuntimeException(&quot;UnChecked Exception&quot;);
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Java] : String, StringBuilder, StringBuffer]]></title>
            <link>https://velog.io/@gdh_/Java-String-StringBuilder-StringBuffer</link>
            <guid>https://velog.io/@gdh_/Java-String-StringBuilder-StringBuffer</guid>
            <pubDate>Tue, 19 Mar 2024 11:19:58 GMT</pubDate>
            <description><![CDATA[<h1 id="java--string-stringbuilder-stringbuffer">[Java] : String, StringBuilder, StringBuffer</h1>
<hr>
<p>자바에서 대표적으로 문자열을 다루는 자료형 클래스로 String, StringBuilder, StringBuffer를 지원한다. 위 3가지 클래스 자료형은 모두 문자열을 다루는데 사용되지만, 사용 목적에 따라 쓰임새가 달라진다.</p>
<h2 id="string">String</h2>
<p>자바에서 String은 불변한 문자열을 처리하기 위한 클래스이다. String 객체가 생성되면 그 값은 변경되지 않고 새로운 객체로 추가, 변경된다. String 자료형으로도 + 연산이나 concat()을 사용해 문자열을 이어붙일 수 있지만, 이 방법은 내용이 합쳐진 새로운 String 인스턴스를 생성하게 되어 문자열을 많이 결합할수록 메모리 낭비와 속도 저하라는 단점이 존재한다.</p>
<h2 id="stringbuilder">StringBuilder</h2>
<p>자바에서 StringBuilder는 가변한 문자열을 처리하기 위한 클래스이다. 새로운 문자열을 추가하거나 변경하게 되면 기존 객체로 추가, 변경된다. 동기화를 지원하지 않기 때문에 StringBuffer와 다르게 멀티쓰레드 환경에서 안정적이지 않으며, StringBuilder는 싱글쓰레드 환경에서 StringBuffer 보다 더 빠른 성능을 가진다.</p>
<h2 id="stringbuffer">StringBuffer</h2>
<p>자바에서 StringBuffer는 가변한 문자열을 처리하기 위한 클래스이다. 새로운 문자열을 추가하거나 변경하게 되면 기존 객체로 추가, 변경된다. 동기화 키워드를 지원해 멀티쓰레드 환경에서 안전하다.</p>
<br>

<table>
<thead>
<tr>
<th align="center"></th>
<th align="center">String</th>
<th align="center">StringBuilder</th>
<th align="center">StringBuffer</th>
</tr>
</thead>
<tbody><tr>
<td align="center">선언 방식</td>
<td align="center">String str = &quot;Hello&quot;;</td>
<td align="center">StringBuilder sb = new StringBuilder(&quot;Hello&quot;);</td>
<td align="center">StringBuffer sb = new StringBuffer(&quot;Hello&quot;);</td>
</tr>
<tr>
<td align="center">문자열 추가</td>
<td align="center">str += &quot;World&quot;;</td>
<td align="center">sb.append(&quot;World&quot;);</td>
<td align="center">sb.append(&quot;World&quot;);</td>
</tr>
<tr>
<td align="center">클래스 종류</td>
<td align="center">불변 클래스</td>
<td align="center">가변 클래스</td>
<td align="center">가변 클래스</td>
</tr>
<tr>
<td align="center">사용 목적</td>
<td align="center">문자열 추가와 변경이 발생하지 않는 경우 사용</td>
<td align="center">단일쓰레드 환경에서 문자열 추가 변경이 자주 발생하는 경우 사용</td>
<td align="center">멀티쓰레드 환경에서 문자열 추가 변경이 자주 발생하는 경우 사용</td>
</tr>
</tbody></table>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Java] : 동일성(identity, ==), 동등성(equality, equals())]]></title>
            <link>https://velog.io/@gdh_/Java-%EB%8F%99%EC%9D%BC%EC%84%B1identity-%EB%8F%99%EB%93%B1%EC%84%B1equality-equals</link>
            <guid>https://velog.io/@gdh_/Java-%EB%8F%99%EC%9D%BC%EC%84%B1identity-%EB%8F%99%EB%93%B1%EC%84%B1equality-equals</guid>
            <pubDate>Mon, 18 Mar 2024 10:04:16 GMT</pubDate>
            <description><![CDATA[<h1 id="java--동일성identity--동등성equality-equals">[Java] : 동일성(identity, ==), 동등성(equality, equals())</h1>
<hr>
<h2 id="동일성-identity">동일성 (Identity)</h2>
<p>동일성은 두 객체의 메모리 주소가 같음을 의미한다. == 연산자를 통해 판별하며 두 객체가 완전히 같은 경우를 의미한다.</p>
<h2 id="동등성-equality">동등성 (Equality)</h2>
<p>동등성은 두 객체의 값이 같음을 의미한다. equals() 메소드를 통해 변수가 참조하고 있는 객체의 주소가 다르더라도 내용만 같으면 동등하다고 한다.</p>
<pre><code class="language-java">void 동일성() {
    List&lt;String&gt; list1 = List.of(&quot;a&quot;, &quot;b&quot;, &quot;c&quot;);
    List&lt;String&gt; list2 = list1;

    System.out.println(list1 == list2); // true
    System.out.println(list1.equals(list2)); // true
}

void 동등성() {
    List&lt;String&gt; list1 = List.of(&quot;a&quot;, &quot;b&quot;, &quot;c&quot;);
    List&lt;String&gt; list2 = List.of(&quot;a&quot;, &quot;b&quot;, &quot;c&quot;);

    System.out.println(crews1 == crews2); // false
    System.out.println(crews1.equals(crews2)); // true
}</code></pre>
<ul>
<li><p>동일성() 에서는 list2가 list1을 대입받았기 때문에 메모리 상에서 같은 주소를 가지고 있고, == 비교에서 true를 리턴한다.</p>
</li>
<li><p>동등성() 에서는 list1과 list2 모두 새로운 리스트를 선언했기 때문에 메모리 상 다른 주소를 가지고 있고, == 비교에서는 false를 리턴한다.</p>
</li>
<li><p>동일성이 지켜진다면 동등성은 자연스럽게 지켜지지만, 동등성이 지켜진다해서 동일성이 보장되지는 않는다.</p>
</li>
</ul>
<hr>
<h2 id="equals와-hashcode">equals()와 hashCode()</h2>
<p>동등성은 equals() 메소드를 통해 판별한다. 하지만 equals() 메소드를 오버라이딩 하지 않고 사용했을 때는 == 과 동일하게 사용된다.</p>
<pre><code class="language-java">class Car {
    private final String name;

    public Car(String name) {
        this.name = name;
    }
}

void 동등성_테스트() {
     Car car1 = new Car(&quot;제네시스&quot;);
    Car car2 = new Car(&quot;제네시스&quot;);

    System.out.println(car1.equals(car2)); // false, new로 새로운 객체를 생성했기 때문에 주소값이 다르다.
}</code></pre>
<p>Object 클래스의 equals 메소드는 메모리 내 주소값이 같은지 비교하기 때문에 동등성을 위해 오버라이딩해 사용해야한다.</p>
<pre><code class="language-java"> public boolean equals(Object obj) {
       return (this == obj);
 }

 @Override
 public boolean equals(Object o) {
      if (this == o)
           return true;
       if (o == null || getClass() != o.getClass())
         return false;
       Car car = (Car)o;
     return name.equals(car.name);
 }</code></pre>
<p>객체의 동등성 기준을 재정의 함으로써, 같은 name을 가진 인스턴스를 같은 객체로 인식되게 구현했다. 그러나 만약 HashMap, HashSet등을 사용했을 때는 해쉬값을 비교해 동등성을 비교하므로 hashCode()를 오버라이딩 해야한다.</p>
<pre><code class="language-java">Set&lt;Car&gt; cars = new HashSet&lt;&gt;();
cars.add(new Car(&quot;a&quot;));
cars.add(new Car(&quot;a&quot;));

System.out.println(crews.size()); // 2, 해쉬값으로 비교하기 때문에 생각과는 다르게 중복제거가 되지 않음</code></pre>
<pre><code class="language-java">@Override
public int hashCode() {
    return Objects.hash(name);
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Java] : SOLID (객체 지향 5대원칙)]]></title>
            <link>https://velog.io/@gdh_/Java-SOLID-%EA%B0%9D%EC%B2%B4-%EC%A7%80%ED%96%A5-5%EB%8C%80%EC%9B%90%EC%B9%99</link>
            <guid>https://velog.io/@gdh_/Java-SOLID-%EA%B0%9D%EC%B2%B4-%EC%A7%80%ED%96%A5-5%EB%8C%80%EC%9B%90%EC%B9%99</guid>
            <pubDate>Thu, 14 Mar 2024 10:45:41 GMT</pubDate>
            <description><![CDATA[<h1 id="java-solid-객체-지향-5대-원칙">[Java] SOLID (객체 지향 5대 원칙)</h1>
<hr>
<p>객체 지향 5대 원칙이란, SRP, OCP, LSP, ISP, DIP 5개를 말하며 코드 확장성과 유지 보수 편의성, 복잡성을 제거해 리팩토링에 소요되는 시간을 줄임으로서 개발의 생산성을 높이기 위해 사용한다.</p>
<h2 id="srp-단일-책임-원칙-single-responsibility-principle">SRP 단일 책임 원칙 (Single Responsibility Principle)</h2>
<p>클래스는 단 하나의 기능만 가지며 클래스가 제공하는 모든 서비스는 그 하나의 책임을 수행하는 데 집중되어 있어야 한다는 원칙이다. SRP를 통해 다른 클래스들이 서로 영향을 미치는 연쇄작용을 줄일 수 있으므로 유지보수에 용이하다.</p>
<h2 id="ocp-개방-폐쇄-원칙-open-close-principle">OCP 개방 폐쇄 원칙 (Open Close Principle)</h2>
<p>클래스는 확장에 열려있어야 하며, 수정에는 닫혀있어야 한다는 원칙이다. 요구사항의 변경사항이 발생했을 때, 클래스 확장을 통해 손쉽게 구현하면서, 클래스 수정은 최소화 하도록 프로그램을 작성하는 것이다. 추상화와 다형성을 통해 OCP를 지킬 수 있으며, OCP는 관리가 용이하고 재사용 가능한 코드를 만드는 기반이 된다.</p>
<h2 id="lsp-리스코프-치환-원칙-liskov-substitution-principle">LSP 리스코프 치환 원칙 (Liskov Substitution Principle)</h2>
<p>서브 타입은 언제나 기반(부모) 타입으로 교체할 수 있어야 한다는 원칙이다. 다형성을 이용하기 위해 상위 클래스 타입으로 객체를 선언해 하위 클래스의 인스턴스를 받으면, 업캐스팅된 상태에서 부모의 메소드를 사용해도 동작이 의도대로 흘러가야 한다는 것을 의미한다.</p>
<pre><code class="language-java">public void f() {
    // Collection 인터페이스 타입으로 변수 선언
    Collection collection = new LinkedList();
    data = new HashSet(); // 중간에 전혀 다른 자료형 클래스를 할당해도 호환됨

    modify(data); // 메소드 실행
}

public void modify(Collection collection){
    collection.add(1); // 인터페이스 구현 구조가 잘 잡혀있기 때문에 add 메소드 동작이 각기 자료형에 맞게 보장됨
    // ...
}</code></pre>
<h2 id="isp-인터페이스-분리-원칙-interface-segregation-principle">ISP 인터페이스 분리 원칙 (Interface Segregation Principle)</h2>
<p>인터페이스를 각각 사용에 맞게 분리해야한다는 원칙이다. SRP가 클래스의 단일 책임이라면 ISP는 인터페이스의 단일 책임을 강조하는 것이다. 인터페이스를 사용하는 클래스 기준으로 분리함으로써, 각 클래스의 목적과 용도에 적합한 인터페이스 만을 제공하는 것이다.</p>
<h2 id="dip-의존-역전-원칙-dependency-inversion-principle">DIP 의존 역전 원칙 (Dependency Inversion Principle)</h2>
<p>어떤 클래스를 참조해서 사용하는 상황이 생긴다면, 클래스를 직접 참조하는 것이 아니라 그 대상의 상위 요소(추상 클래스, 인터페이스)를 참조하라는 원칙이다. 의존 관계를 맺을 때, 자주 변화하는 구현 클래스 보다는 변화가 거의 없는 인터페이스에 의존하라는 원칙이다. </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Java] : 객체 지향 프로그래밍]]></title>
            <link>https://velog.io/@gdh_/Java-%EA%B0%9D%EC%B2%B4-%EC%A7%80%ED%96%A5-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D</link>
            <guid>https://velog.io/@gdh_/Java-%EA%B0%9D%EC%B2%B4-%EC%A7%80%ED%96%A5-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%B0%8D</guid>
            <pubDate>Thu, 14 Mar 2024 10:09:27 GMT</pubDate>
            <description><![CDATA[<h1 id="java-객체-지향-프로그래밍">[Java] 객체 지향 프로그래밍</h1>
<hr>
<h2 id="객체-지향-프로그래밍">객체 지향 프로그래밍</h2>
<p>객체 지향 프로그래밍이란 프로그래밍에 필요한 데이터를 추상화시켜 상태와 행위를 가진 객체로 만들고, 객체 간 상호작용을 통해 프로그램을 개발하는 것을 말한다.</p>
<h3 id="객체-지향-프로그래밍의-특징">객체 지향 프로그래밍의 특징</h3>
<ol>
<li><p><strong>추상화</strong> : 객체의 공통적인 속성과 기능을 추출해 정의하는 것. (인터페이스, 추상 클래스)</p>
</li>
<li><p><strong>상속</strong> : 이미 정의된 상위 클래스의 모든 속성과 기능을 하위 클래스가 물려 받는 것. 기존 코드를 재활용해서 사용함으로써 코드의 생산성을 높일 수 있다.</p>
</li>
<li><p><strong>다형성</strong> : 어떤 객체의 속성이나 기능이 상황에 따라 다른 역할을 수행할 수 있는 것.</p>
<ul>
<li>오버라이딩 : 상위 클래스의 메소드를 재정의해 사용하는 것 </li>
<li>오버로딩 : 같은 이름의 메소드를 매개변수를 다르게 정의해 사용하는 것.</li>
</ul>
</li>
<li><p><strong>캡슐화</strong> : 서로 연관있는 속성과 기능을 하나의 캡슐로 만들어 데이터를 외부로부터 보호하는 것. <strong>데이터 보호</strong>(외부로부터 클래스에 정의된 속성과 기능들을 보호)와 <strong>데이터 은닉</strong>(내부의 동작을 감추고 외부에는 필요한 부분만 노출)를 위해 캡슐화를 활용한다.</p>
</li>
</ol>
<h3 id="객체-지향-프로그래밍의-장단점">객체 지향 프로그래밍의 장단점</h3>
<h4 id="장점">장점</h4>
<ul>
<li><p>코드 재사용 용이, 생산성 향상 : 모듈화된 객체, 그리고 상속을 통해 코드의 재사용성을 높일 수 있고 생산성도 향상된다.</p>
</li>
<li><p>유지보수 용이 : 프로그램 수정, 추가시에도 캡슐화를 통해 주변에 영향이 덜하기 때문에 유지보수가 용이하다.</p>
</li>
</ul>
<h4 id="단점">단점</h4>
<ul>
<li><p>실행 속도 느림 : 전반적으로 객체 지향 언어는 절차 지향 언어보다 상대적으로 실행속도가 느리다. </p>
</li>
<li><p>설계에 많은 시간 소요 : 프로그램 설계시 클래스별, 객체별, 상속 등의 구조를 모두 설계해야하므로 절차 지향 언어에 비해 설계 시간이 많이 소요된다.</p>
</li>
</ul>
<hr>
<h2 id="절차-지향-프로그래밍">절차 지향 프로그래밍</h2>
<p>순차적인 처리가 중요시되며 프로그램 전체가 유기적으로 연결되도록 만드는 프로그래밍 기법.</p>
<h3 id="절차-지향-프로그래밍의-장단점">절차 지향 프로그래밍의 장단점</h3>
<h4 id="장점-1">장점</h4>
<ul>
<li>실행 속도 빠름 : 컴퓨터의 처리 구조와 유사해 실행 속도가 빠르다.</li>
</ul>
<h4 id="단점-1">단점</h4>
<ul>
<li>어려운 유지 보수와 디버깅 : 실행 순서가 정해져 있으므로 코드의 순서가 바뀌면 동일한 결과를 보장하기 어렵고, 따라서 유지 보수와 디버깅이 어렵다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Java] : 접근 제어자, 기타 제어자]]></title>
            <link>https://velog.io/@gdh_/Java-%EC%A0%91%EA%B7%BC-%EC%A0%9C%EC%96%B4%EC%9E%90-%EA%B8%B0%ED%83%80-%EC%A0%9C%EC%96%B4%EC%9E%90</link>
            <guid>https://velog.io/@gdh_/Java-%EC%A0%91%EA%B7%BC-%EC%A0%9C%EC%96%B4%EC%9E%90-%EA%B8%B0%ED%83%80-%EC%A0%9C%EC%96%B4%EC%9E%90</guid>
            <pubDate>Wed, 13 Mar 2024 10:54:45 GMT</pubDate>
            <description><![CDATA[<h1 id="java--접근-제어자-기타-제어자">[Java] : 접근 제어자, 기타 제어자</h1>
<hr>
<h2 id="제어자modifier">제어자(Modifier)</h2>
<p>제어자란 클래스와 클래스 멤버(필드, 메소드, 생성자)의 선언 시 사용하여 부가적인 의미를 부여하는 키워드를 의미한다. 자바에서 제어자는 접근 제어자와 기타 제어자로 구분할 수 있다.</p>
<hr>
<h2 id="접근-제어자">접근 제어자</h2>
<p>접근제어자는 클래스와 클래스의 멤버를 사용할 때, 접근할 수 있는 범위를 지정하는 역할을 한다. 그렇게 함으로써 클래스 외부에서의 직접적인 접근을 허용하지 않는 멤버를 설정해 캡슐화가 가능하다.</p>
<h3 id="접근-제어자의-종류">접근 제어자의 종류</h3>
<ul>
<li>public : 접근 제한 없음</li>
<li>protected : 같은 패키지 내에서, 자손 클래스에서 접근 가능</li>
<li>default : 같은 패키지 내에서만 접근 가능</li>
<li>private : 같은 클래스 내에서만 접근 가능</li>
</ul>
<p>public &gt; protected &gt; default &gt; private 순으로 범위가 넓다.</p>
<table>
<thead>
<tr>
<th align="center">종류</th>
<th align="center">같은 클래스</th>
<th align="center">같은 패키지</th>
<th align="center">자식 클래스</th>
<th align="center">그 외 영역</th>
</tr>
</thead>
<tbody><tr>
<td align="center">public</td>
<td align="center">O</td>
<td align="center">O</td>
<td align="center">O</td>
<td align="center">O</td>
</tr>
<tr>
<td align="center">protected</td>
<td align="center">O</td>
<td align="center">O</td>
<td align="center">O</td>
<td align="center">X</td>
</tr>
<tr>
<td align="center">default</td>
<td align="center">O</td>
<td align="center">O</td>
<td align="center">X</td>
<td align="center">X</td>
</tr>
<tr>
<td align="center">private</td>
<td align="center">O</td>
<td align="center">X</td>
<td align="center">X</td>
<td align="center">X</td>
</tr>
</tbody></table>
<hr>
<h2 id="기타-제어자">기타 제어자</h2>
<h3 id="final-제어자">final 제어자</h3>
<p>자바에서 final 제어자는 변경할 수 없다는 의미로 사용된다. </p>
<ul>
<li>클래스에 사용 : 다른 클래스가 상속받을 수 없는 클래스가 된다.</li>
<li>메소드에 사용 : 오버라이딩을 통한 재정의를 할 수 없는 메소드가 된다.</li>
<li>필드나 지역변수에 사용 : 값을 변경할 수 없는 상수가 된다.</li>
</ul>
<h3 id="static-제어자">static 제어자</h3>
<p>자바에서 static 제어자는 공통적인 이라는 의미로 사용된다. 프로그램 시작 시 최초 한 번만 생성되고 초기화 된다. 또한 클래스의 모든 인스턴스가 공유하며 인스턴스 생성 없이 사용 가능하다.</p>
<ul>
<li>메소드에 사용 : 객체 생성없이 사용 가능한 클래스 메소드로 만들어 준다.</li>
<li>변수에 사용 : 생성한 각 객체들이 값을 공유하는 클래스 변수로 만들어 준다.</li>
</ul>
<h3 id="abstract-제어자">abstract 제어자</h3>
<p>자바에서 abstract 제어자는 추상적인 이라는 의미로 사용된다. 선언부만 있고 구현부가 없는 메소드인 추상 메소드를 정의할 때 쓰이며, 하나 이상의 추상 메소드를 포함하는 추상 클래스를 정의할 때 사용된다.</p>
<table>
<thead>
<tr>
<th align="center">대상</th>
<th align="center">사용 가능한 제어자</th>
</tr>
</thead>
<tbody><tr>
<td align="center">클래스</td>
<td align="center">public, default, final, abstract</td>
</tr>
<tr>
<td align="center">메소드</td>
<td align="center">모든 접근 제어자, final, static, abstract</td>
</tr>
<tr>
<td align="center">필드</td>
<td align="center">모든 접근 제어자, final, static</td>
</tr>
<tr>
<td align="center">지역 변수</td>
<td align="center">final</td>
</tr>
</tbody></table>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Java] : 자바의 원시타입, 참조타입]]></title>
            <link>https://velog.io/@gdh_/Java-%EC%9E%90%EB%B0%94%EC%9D%98-%EC%9B%90%EC%8B%9C%ED%83%80%EC%9E%85-%EC%B0%B8%EC%A1%B0%ED%83%80%EC%9E%85</link>
            <guid>https://velog.io/@gdh_/Java-%EC%9E%90%EB%B0%94%EC%9D%98-%EC%9B%90%EC%8B%9C%ED%83%80%EC%9E%85-%EC%B0%B8%EC%A1%B0%ED%83%80%EC%9E%85</guid>
            <pubDate>Tue, 12 Mar 2024 11:36:54 GMT</pubDate>
            <description><![CDATA[<h1 id="java--자바의-원시타입-참조타입">[Java] : 자바의 원시타입, 참조타입</h1>
<hr>
<p>자바에서는 데이터 타입에 크게 두가지 원시타입과 참조타입이 있다.</p>
<h2 id="원시타입primitive-type">원시타입(Primitive Type)</h2>
<p>원시타입은 정수, 실수, 문자, 논리 리터럴 등 실제 데이터 값을 저장하는 타입이다.</p>
<table>
<thead>
<tr>
<th align="center">종류</th>
<th align="center">데이터형</th>
<th align="center">크기</th>
<th align="center">범위</th>
</tr>
</thead>
<tbody><tr>
<td align="center">논리형</td>
<td align="center">boolean</td>
<td align="center">1bit</td>
<td align="center">true or false</td>
</tr>
<tr>
<td align="center">문자형</td>
<td align="center">char</td>
<td align="center">2byte</td>
<td align="center">&#39;\u0000&#39; ~ &#39;\uffff&#39;</td>
</tr>
<tr>
<td align="center">정수형</td>
<td align="center">byte</td>
<td align="center">1byte</td>
<td align="center">-128 ~ 127</td>
</tr>
<tr>
<td align="center">정수형</td>
<td align="center">short</td>
<td align="center">2byte</td>
<td align="center">-32,768 ~ 32,767</td>
</tr>
<tr>
<td align="center">정수형</td>
<td align="center">int</td>
<td align="center">4byte</td>
<td align="center">-2,147,483,648 ~ 2,147,483,647</td>
</tr>
<tr>
<td align="center">정수형</td>
<td align="center">long</td>
<td align="center">8byte</td>
<td align="center">-9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807</td>
</tr>
<tr>
<td align="center">실수형</td>
<td align="center">float</td>
<td align="center">4byte</td>
<td align="center">1.4E-45 ~ 3.4028235E38</td>
</tr>
<tr>
<td align="center">실수형</td>
<td align="center">double</td>
<td align="center">8byte</td>
<td align="center">4.9E-324 ~ 1.7976931348623157E308</td>
</tr>
</tbody></table>
<h2 id="참조-타입reference-type">참조 타입(Reference Type)</h2>
<p>참조타입은 원시타입을 제외한 타입으로, 객체의 주소를 저장하는 타입이다. 자바에서 실제 객체는 JVM의 힙 영역에 저장되며, 참조 타입 변수는 실제 객체 주소를 스택 영역에 저장한다. 그리고 객체를 사용할 때마다 참조 변수에 저장된 객체 주소를 불러와 사용한다.</p>
<h3 id="boxing-unboxing">Boxing, Unboxing</h3>
<p>Boxing은 원시타입을 참조타입으로 변환시키는 것을 말하고, Unboxing은 참조타입을 원시타입으로 변환시키는 것을 말한다.</p>
<pre><code class="language-java">int i = 0;
Integer integer = i;</code></pre>
<h2 id="원시타입과-참조타입의-차이점">원시타입과 참조타입의 차이점</h2>
<ul>
<li><p>Null 포함 가능 여부 : 원시타입은 null을 담을 수 없지만, 참조타입은 가능하다.</p>
<pre><code class="language-java">int i = null; // 불가능
Integer integer = null; // 가능</code></pre>
</li>
<li><p>제네릭타입에서 사용 가능 여부 : 원시타입은 제네릭타입에서 사용할 수 없지만, 참조타입은 가능하다.</p>
<pre><code class="language-java">List&lt;int&gt; list; // 불가능
List&lt;Integer&gt; list; // 가능</code></pre>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[[Java] : 정적 (Static)]]></title>
            <link>https://velog.io/@gdh_/%EC%A0%95%EC%A0%81-Static</link>
            <guid>https://velog.io/@gdh_/%EC%A0%95%EC%A0%81-Static</guid>
            <pubDate>Mon, 11 Mar 2024 10:22:38 GMT</pubDate>
            <description><![CDATA[<h1 id="java-정적-static">[Java] 정적 (Static)</h1>
<hr>
<h2 id="정적-static">정적 (Static)</h2>
<p>자바에서 static은 &#39;클래스의&#39; 또는 &#39;공통적인&#39; 이란 의미를 가지며 static 키워드를 통해 정적 변수와 정적 메소드를 정의할 수 있다.</p>
<h3 id="정적-변수">정적 변수</h3>
<ul>
<li>한 클래스에서 공통적인 값을 유지해야 할 때 선언한다.</li>
<li>클래스가 메모리에 로딩될 때 생성되어 프로그램이 종료될 때 까지 유지된다.</li>
<li>객체 생성 없이 변수명으로 호출이 가능하다.</li>
</ul>
<pre><code class="language-java">public class StaticTest {
    public static final String DESCRIPTION = &quot;static 변수&quot;;
}</code></pre>
<h3 id="정적-메소드">정적 메소드</h3>
<ul>
<li>인스턴스 변수를 사용할 수 없으므로 인스턴스와 관계없는 메소드를 정적 메소드라 한다.</li>
<li>객체 생성 없이 메소드명으로 호출이 가능하다.</li>
</ul>
<pre><code class="language-java">public class StaticTest {
    public static long add(long a, long b) {return a + b;}
    public static long divide(long a, long b) {return a / b;}
}</code></pre>
<hr>
<h2 id="static의-장점">Static의 장점</h2>
<ul>
<li>JVM의 Static 영역에 저장되어 고정된 메모리 영역을 사용하기 때문에 매번 인스턴스를 생성하며 낭비되는 메모리를 줄일 수 있다.</li>
<li>객체를 생성하지 않고 사용가능 하기 때문에 속도가 빠르다는 것이다. 클래스가 메모리에 올라가는 시점에 생성되어 바로 사용이 가능하기에 속도면에서 이점을 가진다.</li>
</ul>
<h2 id="static의-단점">Static의 단점</h2>
<ul>
<li>GC의 관리를 받는 Heap 영역이 아닌 Static 영역에 프로그램 종료시까지 메모리에 할당된 채로 존재하므로 프로그램 성능에 악영향을 준다.</li>
<li>Static은 따로 객체를 생성하지 않고 Static 영역에서 데이터를 불러오므로 객체의 데이터들이 캡슐화 되어야 한다는 객체지향 프로그래밍 원칙을 위반한다.</li>
<li>Static 메서드는 Interface를 구현하는데 사용할 수 없다. Static 메서드는 코드의 재사용성을 높여주는 자바의 유용한 객체지향적 기능(Interface 등)을 사용하는 것을 방해한다.</li>
</ul>
]]></description>
        </item>
    </channel>
</rss>