<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>ho_c</title>
        <link>https://velog.io/</link>
        <description>기록을 쌓아갑니다.</description>
        <lastBuildDate>Mon, 15 May 2023 09:00:00 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>ho_c</title>
            <url>https://images.velog.io/images/ho_c/profile/17021b73-2c92-4bcb-9e1d-0c0b556db7b4/pedro-approves-pedrorc.gif</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. ho_c. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/ho_c" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[Ch07. 객체지향 프로그래밍Ⅳ]]></title>
            <link>https://velog.io/@ho_c/Ch07.-%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-35u5cehe</link>
            <guid>https://velog.io/@ho_c/Ch07.-%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-35u5cehe</guid>
            <pubDate>Mon, 15 May 2023 09:00:00 GMT</pubDate>
            <description><![CDATA[<h1 id="들어가는-말">들어가는 말</h1>
<ul>
<li><p>지난 시간 &#39;상속&#39;에 이어서 이번에는 &#39;다형성&#39;을 공부해보자.</p>
</li>
<li><p>다형성은 상속과 깊은 관계를 가진다. 이 관계 안에서 OOP의 효율을 극대화할 수 있다.</p>
</li>
</ul>
<br/>

<hr>
<h2 id="1-다형성polymorphism">1. 다형성(polymorphism)</h2>
<blockquote>
<p><strong>여러 가지 형태를 가지는 능력</strong></p>
</blockquote>
<ul>
<li><p>다형성은 말 그대로 여러 형태를 가지는 걸 의미한다. </p>
</li>
<li><p>이를 자바에선 <strong>&#39;한 가지 자료형&#39;</strong>으로 <strong>&#39;여러 자료형의 객체&#39;</strong>를 <strong>&#39;참조&#39;</strong>할 수 있도록 구현한다.</p>
</li>
<li><p>그리고 연결된 객체에 따라 서로 다른 메서드들을 호출한다.</p>
</li>
</ul>
<br/>

<h3 id="정의">정의</h3>
<blockquote>
<p>조상클래스 타입의 참조변수에 자손 클래스의 인스턴스의 주소를 저장할 수 있다.</p>
</blockquote>
<ul>
<li><p>참조변수는 주소를 저장한다. ( null or 4byte )</p>
</li>
<li><p>이전까지 인스턴스를 다룰 땐, 인스턴스의 타입과 일치하는 참조변수를 사용해 주소를 저장하고 접근했다.</p>
</li>
<li><p>다형성은 상속관계에서 조상클래스의 참조변수에 자손클래스의 구현된 &#39;인스턴스&#39;의 주소를 저장하는 것이다.</p>
</li>
<li><p>조상 클래스의 참조변수는 자신에게 정의되지 않은 자손의 멤버에는 접근할 수 없다.</p>
</li>
<li><p>반면 오버라이딩된 멤버에는 접근이 가능하다.</p>
</li>
</ul>
<pre><code class="language-java">//조상 클래스
public class Parent {
    int age = 60;

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

//자손 클래스
public class Child extends Parent{
    int age = 20;

    // 오버라이딩 메서드
    void happy(){ System.out.println(&quot;Child happy&quot;); }

    // 자손 메서드
    void angry(){ System.out.println(&quot;Child angry&quot;); }
}

//다형성 구현
public static void main(String[] args){
        // 다형성
        Parent p = new Child();

        p.happy(); // 실행결과 : &quot;Child happy&quot;
        // p.angry(); - 호출 불가능

        // Down-Casting → 사용할 수 있는 멤버의 수가 늘어남.
        Child c = (Child) p;

        System.out.println(p.age); // 실행결과 : 60
        System.out.println(c.age); // 실행결과 : 20
}</code></pre>
<br/>

<p><strong>자손 멤버에 대한 접근 문제</strong></p>
<blockquote>
<p>이전 예제에서 <code>p.angry();</code>는 호출이 불가능했다.
바로 다음의 참조변수의 형변환을 쓰면 호출할 수 있다.</p>
</blockquote>
<pre><code>p.angry(); // 불가능
c.angry(); // 가능</code></pre><ul>
<li><p>상속 관계 안에선 자손 인스턴스가 생성되면 내부적으로 부모 인스턴스도 함께 생성된다. 
그래서 내부적으로 부모의 생성자도 호출된다.</p>
</li>
<li><p>즉, 다형성은 자손 인스턴스 생성이 부모 인스턴스의 생성을 보장하기 때문에 가능하다.</p>
</li>
<li><p>하지만 <code>p</code>에 자손 인스턴스를 연결해도, 상속받은 조상의 멤버에만 접근할 수 있다.
자손에만 정의된 멤버에는 접근이 불가능하다.</p>
</li>
<li><p>결과적으로 참조변수의 타입은 사용할 수 있는 멤버의 수를 결정한다.</p>
</li>
</ul>
<blockquote>
<p><strong>그럼 자손의 참조변수로 부모 인스턴스에 접근하면 되는거 아님?</strong></p>
</blockquote>
<ul>
<li><p>가당치 않다. 참조변수는 말그대로 주소를 저장한 공간이고, 그 타입은 사용할 수 있는 멤버의 수를 말한다.</p>
</li>
<li><p>또한 상속 관계에서 자손의 멤버는 조상의 멤버보다 최소 같거나, 많다. </p>
</li>
<li><p>이 말은 조상에 없는 멤버가 자손에는 존재할 수 있단 것이고, 자바는 이런 변수를 허용하지 않는다.</p>
</li>
<li><p>결과적으로 참조변수가 사용할 수 있는 멤버는 인스턴스보다 같거나, 적어야 한다.</p>
</li>
</ul>
<br/>

<blockquote>
<p><strong>그럼 그냥 참조변수랑 인스턴스 타입 일치시키는게 더 낫지 않나?</strong></p>
</blockquote>
<ul>
<li><p>기능이 변경 또는 확장될 일이 전혀 없다면 그게 편리할 것이다.
하지만, 프로그램의 변경은 늘상 있는 일이다. 오죽하면 유지보수가 그리 중요할까.</p>
</li>
<li><p>바로 여기서 다형성이 중요한 역할을 한다. </p>
</li>
<li><p>다형성에선 같은 메서드라도 연결된 인스턴스가 다르면 그 인스턴스에 설정된 동작으로 실행한다.</p>
</li>
<li><p>즉, 프로그램이 변경되도 참조변수에 꽂는 인스턴스만 교체하면 모든 일이 해결되기 때문이다.</p>
</li>
<li><p>그래서 다형성은 &#39;인터페이스&#39;를 만나야 그 진가를 발휘한다.</p>
</li>
</ul>
<br/>

<hr>
<h3 id="참조변수의-형변환">참조변수의 형변환</h3>
<blockquote>
<p>상속관계 안에서 참조변수도 기본형처럼 형변환이 가능하다.</p>
</blockquote>
<p><strong>Up-Casting : 자손타입 → 조상타입 (생략가능)</strong>
<strong>Down-Casting : 조상타입 → 자손타입 (생략불가)</strong></p>
<br/>

<p><strong>참조변수의 형변환도 <code>()</code>로 캐스팅 연산을 해주면 된다.</strong></p>
<pre><code class="language-java">//조상 클래스
class TV {
    boolean power;
    int channel;

    void power(){ power != power; }
    void channelUp(){ ++channel; }
    void channelDown(){ --channel; }

}

//자손 클래스
class CaptionTV extends TV{
    String text;
    boolean caption;

    void captionActivate(){ caption != caption; }

}

//형변환 구현
public static void main(String[] args){
    Object ob = null;
    TV tv = null;
    CaptionTV cTv1 = new CaptionTV();
    CaptionTV cTv2 = null;

    // up-casting
    tv = cTv1; // tv = (tv)cTv1;
    ob = tv;

    // down-casting
    cTv2 = (CaptionTv)tv; // 생략 불가.

    // 자손 멤버 호출
    ((CaptionTv)tv).captionActivate();
}</code></pre>
<ul>
<li><p>업캐스팅에서 형변환의 생략이 가능한 건, 참조변수가 다룰 수 있는 멤버의 개수가 연결된 인스턴스의 멤버보다 당연히 적기 때문이다.</p>
</li>
<li><p>반대로 다운캐스팅에서 불가능한 건, 참조변수가 다룰 수 있는 멤버가 인스턴스보다 많기 때문에 인스턴스에 없는 멤버를 호출해 문제가 생길 수 있기 때문이다.</p>
</li>
<li><p>따라서 참조변수의 형변환은 인스턴스에 전체 멤버에 대해 사용가능한 멤버의 개수를 조절한 것이다.</p>
</li>
<li><p>업캐스팅 : 사용 가능한 멤버의 수 <strong>&#39;감소&#39;</strong>
다운캐스팅 : 사용 가능한 멤버의 수 <strong>&#39;증가&#39;</strong></p>
</li>
</ul>
<br/>

<hr>
<h3 id="instanceof연산자">instanceof연산자</h3>
<blockquote>
<p>객체가 특정 클래스의 인스턴스인지 확인하는 연산자</p>
</blockquote>
<p><strong><code>참조변수 instanceof 클래스명</code></strong></p>
<ul>
<li><p>참조변수가 가리키는 인스턴스가 무엇인지 모를 때 또는 확실히 알 수 없을 때, 사용한다.</p>
</li>
<li><p>참조변수가 가리키는 &#39;인스턴스&#39;가 오른쪽 클래스 타입에 해당한다면 <code>true</code>를 아니면 <code>false</code>를 반환한다.</p>
</li>
<li><p>참조변수의 값이 null인 경우, false를 반환한다.</p>
</li>
</ul>
<br/> 

<p>** 용도 **</p>
<ul>
<li><strong>참조변수의 클래스의 타입을 확인해서, 다운 캐스팅이 가능한지 확인하는 용도로 쓰인다</strong>.</li>
</ul>
<pre><code class="language-java">class Parent { int age = 65; }
class Son extends Parent { 
        int age = 24;
        void eat(){/* 구현 */}
        ...
        ...
}
class Daughter extends Parent { 
        int age = 15;
        void goSchool(){/* 구현 */}
        ...
        ...
}

public static void main(String[] args){
    Parent p1 = new Son();
    Parent p2 = new Dauther();

    if(p1 instanceof Son){
        Son son = (Son)p1;
        /* son의 멤버를 처리 */
        System.out.println(son.age) // 24
        son.eat();
    } else if (p2 instatnceof Dauther) {
        Daughter daughter = (Daughter)p2
        /* Daughter의 멤버를 처리 */
        System.out.println(daughter.age) // 15
        daughter.goSchool();
    }
}</code></pre>
<br/>

<ul>
<li><strong>상속관계인지 확인</strong><pre><code class="language-java">class GrandParent { /* 구현 */ }
class Parent extends GrandParent { /* 구현 */ }
class Son extends Parent { /* 구현 */ }
</code></pre>
</li>
</ul>
<p>public static void main(String[] args){
        Son son = new Son();</p>
<pre><code>    System.out.println(son instanceof Parent); // true
    System.out.println(son instanceof GrandParent); // true
    System.out.println(son instanceof Object); // true</code></pre><p>}</p>
<pre><code>
&lt;br/&gt;

---

### 참조변수와 인스턴스의 연결

&gt;상속관계에서 까다로운 건, 중복되는 변수 또는 메서드(오버라이딩된)에 대한 처리문제이다.
이전에 메서드 내부에서 `this, super`로 중복 변수를 구분한 것과 같은 맥락이다.

- 중복 메서드 : 참조변수에 상관없이 실제 연결된 인스턴스의 메서드를 호출함.

- 중복 멤버변수 : 참조변수의 타입을 따라 달라짐.

- static멤버 : 이 역시, 참조변수에 따라 달라진다. 그래서 `클래스명.메서드`형식을 쓰는게 좋음.

```java
class Parent {
    int age = 60;
    String who = &quot;Father&quot;;

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

class Child extends Parent{
    int age = 20;
    void happy(){
        System.out.println(&quot;Child happy&quot;);
    }
}

/* 실행 */
public static void main(String[] args) {
     Parent p1 = new Child();
     Parent p2 = new Parent();
     Child c = new Child();

     // 중복 메서드
     p1.happy(); // Child happy
     p2.happy(); // parent happy

     // 중복 멤버 변수
     System.out.println(p1.age); // 60
     System.out.println(c.age); // 20

     // 중복되지 않는 변수
     System.out.println(p1.who); // father
     System.out.println(c.who); // father
}</code></pre><ul>
<li><p>중복되지 않는 경우에는 선택지가 없기 때문에 참조변수에 영향을 받지 않는다.</p>
</li>
<li><p>중복되는 경우만 참조변수에 영향을 받는다는 것만 인지하면 된다.</p>
</li>
</ul>
<br/>

<hr>
<h3 id="응용1-매개변수의-다형성">응용1. 매개변수의 다형성</h3>
<blockquote>
<p>메서드의 매개변수에서도 다형성을 사용할 수 있다.</p>
</blockquote>
<pre><code class="language-java">// 조상
class Product {
    int price; // 제품 가격
    int bonusPoint; // 구매 시, 제공 보너스 점수

    // 생성자
    Product(int price){
        this.price = price;
        bonusPoint = (int)(price/10.0);
    }
}

// 자손 : 제품
class Tv extends Product{}
class Computer extends Product{}
class Audio extends Product{}

class Buyer {
    int money = 1000;
    int bonusPoint = 0;

    void buy(){
        money = money - 물건값
        bonuspoint = bonusPoint + 물건의 보너스 포인트

    };
}</code></pre>
<p>다형성을 사용하지 않고선 구매자(Buyer)가 물건을 사려면 Tv, Computer, Audio 각각 <code>buy()</code>라는 메서드를 구현해줘야 한다. </p>
<br/>

<p><strong>다형성 적용</strong></p>
<pre><code class="language-java">// 조상
class Product {
    int price; // 제품 가격
    int bonusPoint; // 구매 시, 제공 보너스 점수

    // 생성자
    Product(int price){
        this.price = price;
        bonusPoint = (int)(price/10.0);
    }
}

// 자손 : 제품
class Tv extends Product{
        /* 상속받은 멤버
        price, bonusPoint */

        // 상속받은 멤버를 조상 클래스 생성자로 초기화        
        Tv(){ super(100); }
      /*Tv(){
            super.price = 100;
            super.bonusPoint = (int)(super.price/10.0);
        }*/
}

class Computer extends Product{ /*구현*/ }
class Audio extends Product{ /*구현*/ }

// 구매자 - 다형성을 매개변수에 적용
class Buyer {
    int money = 1000;
    int bonusPoint = 0;

    void buy(Product p){
        money = money - p.price;
        bonuspoint = bonusPoint + p.bonusPoint;
    };
}

/*실행*/
public static void main(String[] args){
    Buyer b = new Buyer();

    // 제품 구매
    b.buy(new Tv());
    b.buy(new Computer());

       // 출력
    System.out.println(b.money); // 700
    System.out.println(b.bonusPoint); // 30
}</code></pre>
<ul>
<li><p>다형성을 사용하면 Product클래스를 매개변수로 선언해서, 자손 인스턴스들을 전달할 수 있다. </p>
</li>
<li><p>같은 원리로 매개변수 타입이 Object클래스면 한 메서드로 모든 클래스들을 매개변수로 처리할 수 있다.</p>
</li>
</ul>
<br/>

<hr>
<h3 id="응용2-여러-종류의-객체-배열">응용2. 여러 종류의 객체 배열</h3>
<blockquote>
<p>공통의 조상을 가진 서로 다른 종류의 객체를 배열로 묶어서 처리가 가능하다.</p>
</blockquote>
<pre><code class="language-java">class Product { ... }
class Tv extends Product{}
class Computer extends Product{}
class Audio extends Product{}

// 응용
Product[] p = new Product[3];
p[0] = new Tv();
p[1] = new Computer();
p[2] = new Audio();</code></pre>
<br/>

<hr>
<h3 id="『-핵심-정리-』">『 핵심 정리 』</h3>
<ul>
<li><p>다형성 : 참조변수 1개로 여러 인스턴스를 다룬다.</p>
</li>
<li><p>참조변수의 타입은 참조할 수 있는 객체의 종류와 사용할 수 있는 멤버의 수를 결정한다.</p>
</li>
<li><p>상속 관계 안에선 연결된 인스턴스에 따라, 동명의 메서드라도 동작이 달라질 수 있다. </p>
</li>
<li><p>형변환을 쓰면, 자손에만 있는 멤버에도 접근할 수 있다. 
다만 인스턴스에는 영향을 미치지 않는다.</p>
</li>
<li><p>instanceof연산 결과가 true면 검사한 타입으로 형변환(up or down)이 가능하다.
형변환 유형은 검사 대상과의 관계에 따라 다름.</p>
</li>
<li><p>상속관계에서 중복되는 멤버변수는 참조변수의 타입을 따른다.
멤버메서드는 실제 구현된 인스턴스를 따르며, Static멤버는 참조변수에 영향을 받으니 클래스명으로 다뤄야 한다.</p>
</li>
<li><p>다형성은 매개변수에도 사용 가능하다. 이 경우 조상 타입의 매개변수 하나로, 자손 인스턴스들을 전달받아 처리할 수 있다.</p>
</li>
<li><p>다형성은 배열에도 사용 가능하다. 조상 타입의 배열의 요소에 자손의 주소를 저장할 수 있다.</p>
</li>
</ul>
<br/>

<hr>
<h2 id="2-추상클래스abstract-class">2. 추상클래스(abstract class)</h2>
<br/>

<h3 id="정의-1">정의</h3>
<blockquote>
<p>추상 메서드가 포함된 클래스</p>
</blockquote>
<pre><code class="language-java">abstract class 클래스명 {}</code></pre>
<ul>
<li><p>추상 메서드는 구현되지 않은 &#39;미완성&#39;메서드이다. 
이런 메서드를 포함된 클래스 역시 <strong>&#39;미완성 설계도&#39;</strong>가 된다. 
예외로 완성된 클래스에 <code>abstract</code>를 붙여 추상 클래스로 정의할 수 있다.</p>
</li>
<li><p>*<em>추상 클래스는 인스턴스화 할 수 없다. *</em></p>
</li>
<li><p>추상 클래스를 상속받은 자손 클래스는 추상 메서드를 구현해야만 한다.
그래야 인스턴스를 만들 수 있다.</p>
</li>
<li><p><strong>추상 클래스는 하나의 &#39;틀&#39;이다. **
이 틀을 기준으로 상속 안에서의 **&#39;기능 확장&#39;</strong>이 추상 클래스의 목적이다.</p>
</li>
</ul>
<br/>

<h3 id="추상-메서드">추상 메서드</h3>
<blockquote>
<p>선언부만 작성된 메서드</p>
</blockquote>
<pre><code class="language-java">abstract 리턴타입 메서드명(); // 구현부는 존재하지 않는다.</code></pre>
<ul>
<li><p>메서드의 구현부(실제 수행 내용)를 작성하지 않은 메서드이다.</p>
</li>
<li><p>상속받는 쪽에서 구현내용이 달라질 수도 있기 때문에 추상 메서드를 사용한다.</p>
</li>
<li><p>따라서 실제 구현내용은 상속받는 쪽에서 오버라이딩으로 구현한다. 
조상 클래스는 선언부만 상속한다.</p>
</li>
<li><p>같은 메서드라도 실제 구현한 클래스에 따라 다르게 동작한다.</p>
</li>
<li><p>상속 받는 쪽에서 하나라도 구현하지 않으면, 해당 클래스도 추상 클래스가 된다.</p>
</li>
<li><p>다형성을 이용하면 사용하는 쪽에선 추상메서드를 호출해도, 실제 연결된 인스턴스의 메서드가 호출된다.</p>
</li>
</ul>
<br/>


<p>** 추상 클래스와 메서드 **</p>
<pre><code class="language-java">// 추상 클래스
abstarct class Player {
    // 추상메서드
    abstract void play(Object o);
    abstract void stop();
}

// 추상메서드 구현
class AudioPlayer extends Player{
    // 오버라이딩으로 구현
    void play(Object o) {
        System.out.println(o + &quot;을 실행합니다.&quot;);
    }

    void stop() {
        System.out.println(&quot;재생을 멈춥니다.&quot;);
    }
}

// 추상메서드 미구현
abstract class AbstractPlayer extends Player{
    void play(int pos){ /*구현*/ };
}

// 추상 메서드 호출
public static void main(String[] args) {
    Player p = new AudioPlayer();
    p.play(&quot;Rollin&#39;&quot;); // Rollin&#39;을 재생합니다.
    p.stop(); // 재생을 멈춥니다.
}</code></pre>
<br/>

<h3 id="추상화">추상화</h3>
<ul>
<li><p>추상 클래스를 만드는 일은 &#39;추상화&#39;작업에 해당한다. 추상화를 이해하기 위해선, 그 반대개념인 구체화를 함께 봐야 한다.</p>
</li>
<li><p>일단, 추상 클래스든 상속이든 목적은 &#39;상속관계를 통한 기능확장&#39;이다.</p>
</li>
<li><p>다만 추상 클래스는 &#39;추상화&#39;에 상속은 &#39;구체화&#39;작업에 해당한다.</p>
</li>
<li><p>상속 계층도 안에서 보면 추상클래스는 &#39;조상 클래스&#39;, 상속은 &#39;자손 클래스&#39;의 방향으로 흐른다. </p>
</li>
<li><p>즉, 추상화는 자손에서 사용할 <strong>&#39;조상 클래스&#39;</strong>를 만들고,
상속은 조상 클래스를 사용해 <strong>&#39;자손 클래스&#39;</strong>를 만든다.</p>
</li>
</ul>
<blockquote>
<p><strong>정리하면 다음과 같다.</strong></p>
</blockquote>
<ul>
<li><strong>추상화</strong> : 클래스 간 공통점을 찾아내 공통 조상을 만드는 작업</li>
<li><strong>구체화</strong> : 상속을 통해 클래스를 구현, 확장하는 작업</li>
</ul>
<br/>

<p><strong>추상 클래스의 사용 용도</strong></p>
<blockquote>
<p>그러나 좀만 생각해보면, 추상화 작업으로 만드는 조상이 굳이 &#39;추상 클래스&#39;일 필요는 없다.</p>
</blockquote>
<p>그냥 조상클래스를 정의하고, 자손에서 오버라이딩해서 사용해도 구체화할 수 있다.</p>
<pre><code class="language-java">class Player {
    void play(Object o){};
    void stop(){};
}</code></pre>
<p>심지어 위처럼 <code>{}</code>만 붙여줘도, 추상메서드가 아니게 된다.</p>
<blockquote>
<p><strong>그럼에도 쓰는 이유는 추상 클래스의 &#39;강제성&#39;때문이다.</strong></p>
</blockquote>
<ul>
<li><p>앞서 봤듯, 추상메서드를 구현하지 않으면 해당 클래스는 인스턴스화할 수 없다.</p>
</li>
<li><p>그래서 상속 받는 쪽에서 의도에 맞게 구현하라는 뜻을 내포한다.
일반 클래스면 구현부를 그대로 남겨둘 수 있기 때문에 강제성이 떨어진다.</p>
</li>
<li><p>결과적으로 추상 클래스의 용도는 상속받는 쪽에서의 구현을 강요하기 위해서이다.</p>
</li>
<li><p>추가로 다형성과 객체배열을 이용하면, 추상 클래스의 참조변수로 추상메서드를 실제 인스턴스에 따라서 실행할 수 있다.</p>
</li>
</ul>
<br/>

<hr>
<h3 id="『-핵심-정리-』-1">『 핵심 정리 』</h3>
<ul>
<li><p>추상메서드가 포함되면 그 클래스는 추상클래스가 된다.
물론 추상메서드가 없어도 <code>abstract</code>를 붙여서 추상클래스를 만들 수 있다.</p>
</li>
<li><p>추상클래스는 인스턴스화할 수 없다.</p>
</li>
<li><p>추상메서드는 선언부만 작성된 메서드이다. 빈 <code>{}</code>도 쓰지 않는다.</p>
</li>
<li><p>추상메서드는 상속받는 쪽에서 구현해야된다. 
구현하지 않으면 해당 클래스도 추상클래스가 된다.
따라서 상속받은 클래스들의 인스턴스는 해당 메서드가 구현되어 있다.</p>
</li>
<li><p>추상화는 &#39;공통 조상 클래스&#39;를 만듦. / 구체화는 조상을 확장하는 자손 클래스를 만듦.</p>
</li>
<li><p><strong>추상클래스의 목적</strong> : 상속을 통한 기능 확장</p>
</li>
<li><p>*<em>사용 용도 *</em>
   1) 상속받는 쪽의 상황에 맞게 추상메서드의 구현을 강요하기 위해
  2) 추상클래스 타입 참조변수에 연결된 인스턴스에서 구현된 추상 메서드를 호출하기 위해.(동명의 메서드가 구현되어있기 때문에 캐스팅을 하지 않아도 된다.)</p>
</li>
</ul>
<br/>

<hr>
<h2 id="3-인터페이스interface">3. 인터페이스(interface)</h2>
<p>인터페이스도 일종의 추상클래스로 분류할 수 있다. 
다만 추상화정도는 추상클래스보다 높으며, 그 용도가 사뭇 다르다.</p>
<h3 id="정의-2">정의</h3>
<blockquote>
<p>상수와 추상메서드로만 이뤄진 일종의 추상클래스</p>
</blockquote>
<ul>
<li><p>인터페이스의 멤버는 <strong>&#39;상수, 추상메서드&#39;</strong>만 허용된다.</p>
</li>
<li><p>추상클래스가 &#39;미완성 설계도&#39;라면, 인터페이스는 <strong>&#39;밑그림&#39;</strong>이다.</p>
</li>
<li><table>
<thead>
<tr>
<th align="center">추상화↑</th>
<th align="center">-------------------&gt;</th>
<th align="center">구체화↑</th>
</tr>
</thead>
<tbody><tr>
<td align="center">인터페이스</td>
<td align="center">추상클래스</td>
<td align="center">클래스</td>
</tr>
</tbody></table>
</li>
</ul>
<ul>
<li>인터페이스도 구현을 통해 기능을 확장하지만, 포인트는 <strong>&#39;기본 규격&#39;</strong>이다.</li>
</ul>
<br/>

<h4 id="인터페이스-작성">인터페이스 작성</h4>
<pre><code class="language-java">interface 인터페이스명 {
    // 상수
    public static final 타입 상수명 = 리터럴;

    // 추상메서드
    public abstract 메서드명();
}</code></pre>
<br/>

<p><strong>제약조건</strong></p>
<blockquote>
<p>인터페이스 작성 시엔, 제약사항이 있다.</p>
</blockquote>
<ul>
<li><p>인터페이스의 접근제어자는 pubilc, default만 가능하다. 
(구현하려면 어디서든지 접근이 가능해야된다.)</p>
</li>
<li><p>모든 멤버변수의 제어자는 <strong><code>public static final</code></strong>이다. (생략 가능)</p>
</li>
<li><p>모든 메서드의 제어자는 <strong><code>public abstract</code></strong>이다. (생략 가능)</p>
</li>
<li><p>단, jdk1.8부터는 static, default메서드도 추가할 수 있게 되었다.</p>
</li>
</ul>
<br/>

<hr>
<h3 id="인터페이스의-상속">인터페이스의 상속</h3>
<blockquote>
<p>인터페이스끼리는 다중 상속이 가능하다.</p>
</blockquote>
<pre><code class="language-java">interface Movable {
    void move(int x, int y);
}

interface Attackable {
    void attack(Unit u);
}

interface Fightable extends Movable, Attackable{}
/*Fightable는 Movable, Attackable에 정의된 멤버를 모두 상속받는다.*/</code></pre>
<br/>

<h3 id="인터페이스-구현">인터페이스 구현</h3>
<blockquote>
<p>인터페이스는 빈 껍데기이다. 그래서 일반 클래스로 인터페이스를 <strong>구현해야된다.</strong></p>
</blockquote>
<pre><code class="language-java">// 인터페이스
interface Fightable extends Movable, Attackable{}

// 구현체
class Fighter implements Fightable{
    public void move(int x, int y){ /*구현*/ };
    public void attack(Unit u){ /*구현*/ };
}</code></pre>
<ul>
<li><p>인터페이스는 만든 부분이 없어서 인스턴스화 시킬 수 없다. 그래서 구현해야한다.</p>
</li>
<li><p>구현은 추상클래스를 상속받아 미구현된 부분을 구현하는 것과 다르지 않다.</p>
</li>
<li><p>추상클래스는 미구현된 부분을 구현하거나, 해당 상황에 맞게 변경해서 기능을 확장(extends)한다.</p>
</li>
<li><p>반대로 인터페이스는 전체를 구현(implements)해야 한다.</p>
</li>
<li><p>만약 인터페이스의 일부 메서드만 구현하면, 해당 클래스는 추상클래스가 된다.</p>
</li>
<li><p>상속과 구현은 동시에 가능하다.</p>
<pre><code class="language-java">// 구현체
class Fighter extends Unit implements Fightable{
  public void move(int x, int y){ /*구현*/ };
  public void attack(Unit u){ /*구현*/ };
}</code></pre>
</li>
</ul>
<br/>

<h4 id="상속과-구현의-구조도"><strong>상속과 구현의 구조도</strong></h4>
<ul>
<li><strong>예제</strong></li>
</ul>
<pre><code class="language-java">// 구현체
class Fighter extends Unit implements Fightable{
    public void move(int x, int y){ /*구현*/ };
    public void attack(Unit u){ /*구현*/ };
    /* 상속받는 메서드의 접근제어자보다 범위가 작아선 안된다.
    인터페이스의 접근제한자는 public이기 때문에 구현체에서도 public이 된다.
    */
}

// 조상클래스
class Unit {
    int currentHP;
    int x;
    int y;
}
// 인터페이스
interface Fightable extends Movable, Attackable{}
</code></pre>
<ul>
<li>** 구조도**</li>
</ul>
<p><img src="https://velog.velcdn.com/images/ho_c/post/34903f93-d685-4372-bddc-83949e97e60b/image.jpg" alt=""></p>
<br/>

<hr>
<h3 id="인터페이스-활용한-다중상속">인터페이스 활용한 다중상속</h3>
<blockquote>
<p>인터페이스를 활용하면, 막힌 다중상속을 구현할 수 있다.</p>
</blockquote>
<br/>

<p><strong>다중 상속의 모호성</strong></p>
<ul>
<li><p>다중 상속의 가장 큰 단점은 <strong>&#39;모호성&#39;</strong>이다.</p>
</li>
<li><p>모호성은 상속받는 조상들의 <strong>멤버가 중복(변수명, 메서드 선언부 일치)될 때</strong>, 발생한다.
즉, 어느 조상의 것을 선택할지 애매하다는 것이다.</p>
</li>
<li><p>이를 위해선 한 쪽의 상속을 포기하거나, 충돌하지 않게 조상 클래스를 변경해야 한다.</p>
</li>
<li><p>따라서 자바는 단점이 이점보다 크다고 판단해, 다중 상속을 막았다.</p>
</li>
</ul>
<br/>

<p><strong>다중 상속 구현</strong></p>
<blockquote>
<p>인터페이스 자체는 다중 상속을 구현하는 용도가 아니라는 점을 알아두자.</p>
</blockquote>
<ul>
<li><p>인터페이스와 조상클래스 간에는 부딪힐 일이 없다. </p>
</li>
<li><p>멤버변수는 static 상수이니 클래스명으로 구분이 가능하고, 
메서드는 조상 클래스의 구현된 걸 상속받으면 된다.</p>
</li>
<li><p>즉, 인터페이스와 클래스는 부딪힐 일이 없다는 것이다.</p>
</li>
<li><p>그래서 다중 상속은 상속, 포함 관계 그리고 인터페이스 구현을 조합해서 가능하다.</p>
</li>
</ul>
<pre><code class="language-java">// 조상 클래스 1
public class Tv{
    protected boolean power;
    protected int channel;
    protected int volume;

    public void power() { /*구현*/ }
    public void channelUp() { /*구현*/ }
    public void channelDown() { /*구현*/ }
    public void volumeUp() { /*구현*/ }
    public void volumeDown() { /*구현*/ }
}

// 인터페이스 : VCR클래스의 인터페이스(규격)
public interface IVCR {
    public void play();
    public void stop();
    public void reset();
    public int getCounter();
    public void setCounter(int c);
}

// 조상 클래스 2
public class VCR implements IVCR {
    protected int counter;

    public void play() { /*구현*/ }
    public void stop() { /*구현*/ }
    public void reset() { /*구현*/ }
    public int getCounter() { return this.counter; }
    public void setCounter(int c) { this.counter = c; }
}


// 다중 상속을 활용한 구현체
public class TVCR extends Tv implements IVCR {
    IVCR vcr = new VCR(); // 포함 관계 및 다형성

    // 인터페이스 구현 → 구현된 메서드는 호출 시, VCR 인스턴스의 메서드를 재호출한다.
    public void play() { vcr.play(); };
    public void stop() { vcr.stop(); };
    public void reset() { vcr.reset();};
    public int getCounter() { return vcr.getCounter(); };
    public void setCounter(int c) { vcr.setCounter(c); }
}</code></pre>
<blockquote>
</blockquote>
<ul>
<li>책에선 인터페이스 없이도 포함관계로도 충분하며, 단지 다형적 특성을 살리기 위해 인터페이스를 사용한다고 설명한다.</li>
<li>또 책의 예제에선 IVCR은 VCR클래스의 카피본이었지만, 이 경우에는 다형적 특성을 잘 살리지 못한다고 판단했다.</li>
<li>난 반대로 VCR를 IVCR의 구현체로 활용했다. 그래서 IVCR만 구현하면 다중상속에서 포함되는 인스턴스만 변경하여 다형적 특성을 더 살리도록 했다.</li>
</ul>
<br/>


<hr>
<h3 id="인터페이스의-다형성">인터페이스의 다형성</h3>
<pre><code class="language-java">Fightable f = new Fighter(); // (Fightable)new Fighter();</code></pre>
<ul>
<li><p>다형성으로 인해 조상타입의 참조변수에 자손 인스턴스의 주소를 저장할 수 있다. (참조)</p>
</li>
<li><p>인터페이스로도 다형성이 동일하게 가능하다. (인터페이스 → 구현체)</p>
</li>
<li><p>또한, 인터페이스로 형변환(up,down)이 가능하다.</p>
</li>
<li><p>같은 원리로 메서드의 매개변수, 리턴타입으로도 사용될 수 있다.</p>
</li>
</ul>
<pre><code class="language-java">// 매개변수
void attack(Fightable f){
    ...
}

// 리턴타입
Fightable method(){
    ...
    Fightable f = new Fighter();
    return f;
}</code></pre>
<ul>
<li>리턴타입으로 사용 시, 인터페이스를 반환하는 것이 아닌 해당 인터페이스를 구현한 인스턴스의 주소를 반환해야 한다.</li>
</ul>
<br/>

<p><strong>규격으로서의 인터페이스</strong></p>
<blockquote>
<p>드디어 인터페이스가 &#39;다형성&#39;을 만났다. 
이제 예제를 통해 <strong>&#39;기본 규격&#39;</strong>으로서 인터페이스가 어떻게 활용되는지 살펴보자.</p>
</blockquote>
<pre><code class="language-java">// 인터페이스
interface DBAPI {
    // 검색
    public abstract String search(String word);
    // 쓰기
    public abstract int write(String article);
    // 수정
    public abstract int update(String article);
    // 삭제
    public abstract int delete();
}

// 구현체 1 : 오라클 DB
class OracleDB implements DBAPI {
    // 검색
    public String search(String word){
        /*구현*/
        return article; // DB에서 불러온 글을 String article에 저장해서 반환. 
    };
    // 쓰기
    public int write(String article){
        boolean result;
        /*구현*/
        if(result){
            return 1; // 작업 결과가 성공이면 1을 반환
        }else{
            return 0; // 실패 시, 0을 반환
        }
    };
    // 수정
    public int update(String article){
           boolean result;
        /*구현*/
        if(result){
            return 1; // 작업 결과가 성공이면 1을 반환
        }else{
            return 0; // 실패 시, 0을 반환
        }
    };
    // 삭제
    public int delete(){
        boolean result;
        /*구현*/
        if(result){
            return 1; // 작업 결과가 성공이면 1을 반환
        }else{
            return 0; // 실패 시, 0을 반환
        }
    };
}

// 구현체 2 : MariaDB
class MariaDB implements DBAPI {
    // 검색
    public String search(String word){ /*위와 동일*/ };
    // 쓰기
    public int write(String article){ /*위와 동일*/ };
    // 수정
    public int update(String article){ /*위와 동일*/ };
    // 삭제
    public int delete(){ /*위와 동일*/ };
}

// main();
public static void main(String[] args){
    DBAPI db = new Oracle();
    db.search(&quot;Interface&quot;);
    db.write(&quot;현재 인터페이스 공부 중입니다.&quot;);
    db.update(&quot;현자 자바 공부 중입니다.&quot;)
    db.delete();
};</code></pre>
<ul>
<li><p>DB는 기본적으로 CRUD가 가능해야된다. 즉, CRUD는 DB라면 가져야 하는 기본 규격이다.
그래서 DBAPI 인터페이스는 CRUD를 구현하도록 작성되어 있다.</p>
</li>
<li><p>그리고 각 구현체의 실제 구현된 부분은 DB에 따라 다르다.</p>
</li>
<li><p>main에서 만약 DB를 MariaDB로 교체할 경우, <code>DBAPI db = new MariaDB();</code>로 변경해주면 된다. 그러면 참조변수 <code>db</code>를 이용한 모든 작업들이 MariaDB로 변경되서 수행된다.</p>
</li>
<li><p>이렇게 인터페이스와 다형성을 함께 활용하면, 호스트-서버 환경에서 서버 코드를 호스트 측에 영향을 미치지 않고 변경할 수 있다.</p>
</li>
</ul>
<br/>

<hr>
<h3 id="인터페이스-장점">인터페이스 장점</h3>
<br/>

<p><strong>1. 개발 시간 단축</strong></p>
<p>인터페이스는 작성만 해도 다른 곳에서 사용할 수 있다. 
그래서 메서드 구현 / 호출, 양쪽에서 독립적인 동시 개발이 가능하다.
구현하는 측은 구현만 하면되고, 호출 측은 선언부만 알아서 input, output만 확인하면 된다.</p>
<br/>

<p><strong>2. 표준화</strong></p>
<p>앞서 본 <strong>&#39;규격&#39;</strong>이 클래스 단위의 규격이라면 <strong>&#39;표준화&#39;</strong>는 프로젝트 전체의 규격이다.
즉, 프로젝트의 틀을 인터페이스로 작성하고 개발자는 이를 따라 구현한다.</p>
<blockquote>
<p><strong>예를 들어 ERP프로그램을 개발하게 됐다 하자.</strong>
고객사마다 세부적인 부분은 다르겠지만, ERP의 기본 기능은 표준화된 인터페이스를 구현하면된다.
그 후, 다른 부분들을 추가 개발하면 될 것이다.</p>
<blockquote>
<p><strong>이처럼 인터페이스를 이용하면 일관되고 정형화된 프로그램을 개발할 수 있다.</strong></p>
</blockquote>
</blockquote>
<br/>

<p><strong>3. 서로 관계없는 클래스들 간의 관계를 만들 수 있다.</strong></p>
<p>인터페이스가 클래스 간 <strong>&#39;다리&#39;</strong>를 놔준다고 생각하자.
일단 상속은 <strong>&#39;기능 확장&#39;</strong>에 초점이 맞춰져 있다. 그래서 조상-자손, 공통조상을 가진 자손들은 서로 공통점이 있어야 한다.
하지만 공통점 없는 클래스도 인터페이스만 있으면, 관계가 성립될 수 있다. 한마디로 하나로 묶어서 써먹을 수 있다는 것이다.</p>
<p><strong>예시</strong></p>
<pre><code class="language-java">// 조상 클래스
class Animal{ /*내용생략*/ }
class LandAnimal extends Animal{ /*내용생략*/ }
class MarineAnimal extends Animal{ /*내용생략*/ }

// 육지 동물
class Giraffe extends LandAnimal{ /*내용생략*/ }
class Koala extends LandAnimal{ /*내용생략*/ }
class Lion extends LandAnimal{ /*내용생략*/ }

// 해양 동물
class Shark extends MarineAnimal{ /*내용생략*/ }
class Seahorse extends MarineAnimal{ /*내용생략*/ }</code></pre>
<p><code>LandAnimal</code>, <code>MarineAnimal</code>는 <code>Animal</code>를 상속받고, 다시 두 클래스를 상속받는 기린, 코알라, 사자, 상어, 해마가 있다. 
여기서 우리는 &#39;육식 행위&#39;를 하는 메서드를 구현하고 싶은데, 사자와 상어만 해당된다.
하지만 이 둘을 연결해줄 접점은 Animal 하나뿐인데 그렇게 되면 채식동물도 육식을 하게 된다.
또, 오버로딩으로 메서드 구현을 하더라도 결국 2개는 만들어야 된다.
<strong>이런 경우 &#39;인터페이스&#39;로 연결을 시켜준다.</strong></p>
<pre><code class="language-java">interface MeetAble {}
class Lion extends LandAnimal implements MeetAble{ /*내용생략*/ }
class Shark extends MarineAnimal implements MeetAble{ /*내용생략*/ }

// 메서드 구현
void eatMeet(MeetAble name){
    /*육식 행위 구현*/
}</code></pre>
<blockquote>
<p>따라서 상속관계가 아니지만, 공통점이 있을 때 인터페이스를 사용해 관계를 맺어준다.
그리고 인터페이스를 기준으로 코드가 관리되므로, 코드 중복도가 낮아지고(오버로딩 안해도 됨), 재사용성과 유지보수성이 높아진다.(인터페이스에 코드를 모았기때문에 인터페이스로 구현체들을 관리할 수 있다.)</p>
</blockquote>
<br/>

<p><strong>4. 독립적인 프로그래밍이 가능</strong></p>
<p>상속, 포함은 선언과 구현이 연결되어 있어 구현이 변경될 경우, 그 클래스가 선언되어 사용되는 곳도 변경해야될 수도 있다. 
쉽게 말하면 어떤 클래스 내부에서 다른 클래스의 인스턴스를 사용하는 순간, 클래스 간의 의존관계가 만들어진다.</p>
<p>하지만 인터페이스를 사용하면, 앞선 DB예제처럼 구현이 바뀌더라도 선언된 곳에서는 변경할 필요가 없어진다.
따라서 말 그대로 인터페이스로 클래스 간을 연결해서 한쪽이 변경되더라도 영향을 미치지 않도록 만든다.</p>
<br/>

<hr>
<h3 id="인터페이스-정리">인터페이스 정리</h3>
<blockquote>
<p><strong>인터페이스 장점4에 해당하는 내용이다.</strong></p>
</blockquote>
<br/>

<p><strong>먼저 인터페이스를 사용하기 위한 두 가지 사항을 기억해두자.</strong></p>
<p>① 클래스를 사용하는 쪽(User)과 클래스를 제공하는 쪽(Provider)이 있다.
② 메서드를 호출하는 쪽(User)는 메서드(Provider)의 선언부만 알면 된다.</p>
<br/>

<p><strong>클래스의 직접 관계</strong></p>
<pre><code class="language-java">class A {
    pubilc void methodA(B b){
        b.methodB(); // 의존 관계 발생
    }
}
class B {
    public void methodB() {
        System.out.println(&quot;methodB()&quot;);
    }
}

// main
public static void main(String[] args){
    A a = new A();
    a.methodA(new B());
}</code></pre>
<blockquote>
<p>위 예제를 구조로 표현하면 다음과 같다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/ho_c/post/38fdbb5d-b4f8-4c6f-9676-9c283dea8227/image.jpg" alt=""></p>
<br/>

<p><strong>인터페이스를 이용한 간접 관계</strong></p>
<pre><code class="language-java">interface I {
    public abstract void methodB();
}

class B implements I {
    public void methodB(){
           System.out.println(&quot;methodB()&quot;);
    };
}

class A {
    pubilc void methodA(I i){
        i.methodB(); // 의존 관계 발생
    }
}

// main
public static void main(String[] args){
    A a = new A();
    I i = new B();
    a.methodA(i);
}</code></pre>
<blockquote>
<p>구조로 표현하면 다음과 같다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/ho_c/post/800ea4c2-73c4-4e81-9861-035e54ed4a8b/image.jpg" alt=""></p>
<ul>
<li><p>코드상 클래스A 내부에는 클래스B가 사용되지 않았고, 그 결과 직접적인 관계는 맺어지지 않았다.</p>
</li>
<li><p>I의 구현체는 A에겐 중요하지 않다. 구현되지 않아도 A는 메서드를 호출할 수 있고, 이름을 몰라도 알아서 호출된다.</p>
</li>
<li><p>또한 B가 아니어도 된다. 따라서 A와 B는 독립적인 프로그래밍이 가능하다.</p>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/ho_c/post/ce7bdb78-0f67-41f1-8361-c64fead749dc/image.jpg" alt=""></p>
<br/>

<hr>
<blockquote>
<p>원래 인터페이스에 추상메서드를 제외하고는 사용할 수 없었다. 
근데 JDK1.8부터 정적 메서드와 디폴트 메서드를 추가할 수 있게 되었다.</p>
</blockquote>
<h4 id="디폴트-메서드">디폴트 메서드</h4>
<ul>
<li><p>상속 관계에서 메서드 추가는 조상의 멤버추가로 자손에서 오버라이딩할지 말지만 결정하면 된다.</p>
</li>
<li><p>반면 인터페이스는 추상메서드가 추가되는거라, 구현체에서 구현할게 더 생기는 큰 일이다.</p>
</li>
<li><p>그래서 나온게 Default Method이다. 디폴트 메서드는 추상메서드가 아니라 구현체에서 구현할 필요가 없다.</p>
</li>
<li><p>default라는 키워드가 붙으며, 접근제어자는 public으로 생략 가능이다.
구현부의 <code>{}</code>까지 있어야 한다.</p>
</li>
</ul>
<pre><code class="language-java">// 인터페이스
interface DefaultInterface{
    // 추상메서드
    void method(); 
    // 디폴트메서드
       default void newMethod(){
        System.out.println(&quot;디폴트 메서드&quot;);    
    }; 
}

// 구현체
public class DefaultClass implements DefaultInterface{
    public void method() {
        System.out.println(&quot;구현 완료&quot;);
    }
}

// 실행
public static void main(String[] args) {
    DefaultInterface d = new DefaultClass();
    d.method(); // 구현 완료
    d.newMethod(); // 디폴트 메서드
}</code></pre>
<p><strong>주의사항</strong></p>
<blockquote>
<p>디폴트 메서드도 인터페이스(다중 상속 가능), 조상 클래스(상속과 인터페이스 동시 가능)의 메서드와 이름이 겹쳐서 충돌할 수 있다.</p>
</blockquote>
<ol>
<li><strong>여러 인터페이스의 디폴트 메서드 간의 충돌</strong>
 : 뭐를 따를지 모르니까 구현체에서 오버라이딩해야 한다.</li>
</ol>
<ol start="2">
<li><strong>디폴트 메서드와 조상 클래스의 메서드 간의 충돌</strong>
 : 조상이 먼저다. 그래서 조상 클래스의 메서드 상속, 디폴트 메서드는 무시</li>
</ol>
<br/>

<h4 id="static-메서드">static 메서드</h4>
<ul>
<li><p>정적 메서드 자체는 인스턴스랑 관계가 없기 때문에, 인터페이스에 못 쓸 이유가 없다.</p>
</li>
<li><p>인터페이스에 쓰이는 정적메서드의 접근 제어자는 public이며, 생략할 수 있다.</p>
</li>
</ul>
<br/>

<hr>
<h3 id="『-핵심-정리-』-2">『 핵심 정리 』</h3>
<ul>
<li><p>인터페이스의 접근제어자는 <code>pubilc, default</code>만 가능하다.
멤버의 제어자는 대부분 생략가능하다.</p>
</li>
<li><p>인터페이스의 멤버는 상수, 추상메서드, 정적 메서드와 기본 메서드만 가능하다.</p>
</li>
</ul>
<ul>
<li>인터페이스 간의 다중 상속이 가능하다.</li>
</ul>
<ul>
<li>인터페이스의 일부만 구현한다면, 해당 클래스는 abstract가 붙어 추상클래스가 된다.</li>
</ul>
<ul>
<li><p>인터페이스의 구현체는 상속과 구현을 동시에 할 수 있다.</p>
</li>
<li><p>구현체의 메서드의 접근 제어자는 반드시 public이어야 한다.
오버라이딩 시, 조상의 메서드보다 넓은 범위의 접근 제어자를 지정해야 한다.</p>
</li>
<li><p>인터페이스에서도 다형성을 적용할 수 있다. </p>
</li>
<li><p>*(참조변수, 형변환, 메서드의 리턴타입 and 매개변수)</p>
</li>
<li><p>*</p>
</li>
<li><p>반환타입인 인터페이스인 건, 해당 인터페이스 구현체의 인스턴스를 반환한다는 것이다.</p>
</li>
<li><p>인터페이스와 다형성을 활용하면, 호스트 측에 상관없이 서버 코드만 변경할 수 있다.</p>
</li>
<li><p>서로 관계 없는 클래스들을 빈 인터페이스로 관계를 맺어줄 수 있다.</p>
</li>
<li><p>인터페이스 사용 시, 메서드를 이용하는 쪽은 <strong>&#39;선언부&#39;</strong>만 알고 있어도 사용할 수 있다.</p>
</li>
<li><p>인터페이스를 이용해서 클래스 간의 관계를 맺어주면, 독립적인 프로그래밍이 가능하다.</p>
</li>
<li><p><em>(호출하는 쪽은 구현체가 아닌 인터페이스를 의존하게 된다.)*</em></p>
</li>
<li><p>디폴트 메서드는 구현체에서 구현하지 않아도 된다.</p>
</li>
<li><p>정적 메서드도 public접근제어자를 붙여 인터페이스에 작성할 수 있다.</p>
</li>
</ul>
<br/>

<hr>
<h2 id="4-내부-클래스inner-class">4. 내부 클래스(inner class)</h2>
<h3 id="정의-3">정의</h3>
<blockquote>
<p>클래스 내에 선언되는 클래스. </p>
</blockquote>
<ul>
<li><p>서로 긴밀한 관계에 있는 클래스 중 한 클래스를 다른 클래스의 내부에 선언하는 것이다.</p>
</li>
<li><p>내부 클래스는 자신이 속해있는 외부 클래스 외에선 잘 사용되지 않아야 한다.</p>
</li>
</ul>
<pre><code class="language-java">class A {
    /*내용 생략*/
}

class B {
    /*내용 생략*/
}

// 내부 클래스로 만들기
class A { // 외부 클래스
    ...
    class B { // 내부 클래스 → A에서만 사용되야 그 목적을 살린다.
        ...
    }
    ...
}</code></pre>
<br/>

<p><strong>장점</strong></p>
<ul>
<li><p>내부 클래스에서 외부 클래스의 멤버들을 쉽게 접근할 수 있다.</p>
</li>
<li><p>내부에 클래스를 숨김으로 코드의 복잡성을 줄일 수 있다.(캡슐화)</p>
</li>
</ul>
<br/>

<hr>
<h3 id="종류와-특징">종류와 특징</h3>
<blockquote>
<p>내부 클래스의 종류는 변수 선언 위치에 따른 종류와 같다.</p>
</blockquote>
<table>
<thead>
<tr>
<th align="left">내부 클래스</th>
<th align="left">선언위치</th>
<th align="center">특징</th>
</tr>
</thead>
<tbody><tr>
<td align="left">인스턴스 클래스</td>
<td align="left">외부 클래스 멤버변수 선언위치</td>
<td align="center">인스턴스 멤버와 관련된 작업에 사용</td>
</tr>
<tr>
<td align="left">스태틱 클래스</td>
<td align="left">외부 클래스 멤버변수 선언위치</td>
<td align="center">static멤버(메서드)와 관련된 작업에 사용</td>
</tr>
<tr>
<td align="left">지역 클래스</td>
<td align="left">외부 클래스의 메서드, 초기화블럭</td>
<td align="center">선언된 영역 내부에서만 사용</td>
</tr>
<tr>
<td align="left">익명 클래스</td>
<td align="left"></td>
<td align="center">선언과 인스턴스화를 동시에 하는 클래스</td>
</tr>
</tbody></table>
<br/>

<h3 id="선언">선언</h3>
<pre><code class="language-java">// 외부 클래스
class Outer{
    class InstanceInner{} // 인스턴스 클래스 - 멤버변수 영역
    static class StaticInner{} // 정적 클래스 - 멤버변수 영역

    void method(){
        class LocalInner{} // 지역 클래스 - 메서드 영역
    }
}</code></pre>
<br/>

<h4 id="내부-클래스의-제어자와-접근성">내부 클래스의 제어자와 접근성</h4>
<ul>
<li><p>내부 클래스도 클래스라 <code>abstract, final</code> 같은 제어자를 사용할 수 있다.</p>
</li>
<li><p>선언 위치에 따라 내부 클래스는 외부 클래스의 멤버로 간주되기도 하고, 메서드 내의 지역 변수처럼 다뤄지기도 한다.</p>
</li>
<li><p>따라서 선언 위치에 따라 멤버 변수처럼 <code>private, protected</code> 같은 접근 제어자도 사용할 수 있다. (일반 클래스는 public, default만 사용가능)</p>
</li>
<li><p>내부 클래스 중, static 클래스만 static멤버를 가질 수 있다.</p>
</li>
<li><p>단, final이 붙은 변수는 상수라서 모든 내부 클래스에서 정의할 수 있다.</p>
</li>
</ul>
<pre><code class="language-java">class Outer{
    class InstanceInner{
        // static int a = 1; 에러
    }
    static class StaticInner{
        static int a = 1;
    } 
    void method(){
        class LocalInner{
            final static int b = 10; // final이 붙어 상수라 가능
        }
    }
}</code></pre>
<ul>
<li>인스턴스 멤버와 static 멤버의 관계처럼 내부 클래스 간의 관계도 같다.</li>
<li><em>(생성 시기가 다름)*</em></li>
</ul>
<table>
<thead>
<tr>
<th align="center">내부 클래스 종류</th>
<th align="center">외부 인스턴스 멤버</th>
<th align="center">외부 static 멤버</th>
<th align="center">인스턴스 클래스 멤버</th>
<th align="center">static 클래스 멤버</th>
<th align="center">메서드 영역</th>
</tr>
</thead>
<tbody><tr>
<td align="center">인스턴스 클래스</td>
<td align="center">객체 생성없이 사용 가능</td>
<td align="center">객체 생성없이 사용 가능</td>
<td align="center">객체 생성으로 사용가능</td>
<td align="center">클래스명.멤버명으로 접근</td>
<td align="center">X</td>
</tr>
<tr>
<td align="center">static 클래스</td>
<td align="center">객체 생성없이 사용 불가</td>
<td align="center">객체 생성없이 사용 가능</td>
<td align="center">X</td>
<td align="center">클래스명.멤버명으로 접근</td>
<td align="center">X</td>
</tr>
<tr>
<td align="center">지역 클래스</td>
<td align="center">O</td>
<td align="center">O</td>
<td align="center">객체 생성으로 사용가능</td>
<td align="center">클래스명.멤버명으로 접근</td>
<td align="center">상수 한정 가능</td>
</tr>
</tbody></table>
<ul>
<li><p>지역 클래스의 경우 선언된 메서드의 지역변수가 상수인 경우에 참조할 수 있다.</p>
</li>
<li><p><em>(소멸된 지역변수를 참조하려는 경우도 있어서)*</em></p>
</li>
<li><p>컴파일하면 <code>외부 클래스명$내부 클래스명.class</code>형식으로 파일명이 붙는다. </p>
</li>
<li><p>단, 지역내부 클래스는 이름이 중복될 경우 클래스명 앞에 숫자를 붙인다.</p>
</li>
<li><p>외부와 내부의 중복되는 변수명은 <code>this</code>로 구분된다.</p>
<pre><code class="language-java">class Outer{
  int value = 10; // Outer.this.value
  class Inner{
      int value = 20; // this.value
  }
}</code></pre>
</li>
</ul>
<br/>

<hr>
<h3 id="익명-클래스">익명 클래스</h3>
<blockquote>
<p><strong>이름 없는 일회용 익명 클래스.</strong></p>
</blockquote>
<pre><code class="language-java">new 조상클래스명(){
    // 멤버 선언
}

new 구현인터페이스명(){
    // 멤버 선언
}</code></pre>
<ul>
<li><p>내부 클래스와 달리 이름이 없다. 대신 구별하기 쉽게 클래스명 뒤에 <code>()</code>이 붙는다.</p>
</li>
<li><p>선언과 생성이 동시에 일어나, 단 한번만 사용이 가능하다.</p>
</li>
<li><p>객체 또한 딱 하나 밖에 생성할 수 있다.</p>
</li>
<li><p>이름이 없어 생성자도 없으며, 조상 클래스명 or 인터페이스명을 사용한다.</p>
</li>
<li><p>그래서 상속과 구현을 동시에 할 수 없고, 하나의 조상만 상속받거나 or 하나의 인터페이스만 구현해야 한다.</p>
</li>
<li><p>클래스 파일명은 <code>외부 클래스명$숫자.class</code>으로 생성된다.</p>
</li>
</ul>
<br/>

<p><strong>예시 1. 익명 클래스의 유형</strong></p>
<pre><code class="language-java">class Anonymous(){
    Object iv = new Object(){ void method(){} };
    static Object cv = new Object(){ void method(){} };

    void myMethod() {
        Object lv = new Object(){ void method(){} };
    }
}</code></pre>
<br/>

<p><strong>예시 2. 익명 클래스로 변환</strong></p>
<pre><code class="language-java">// 기존
class Inner{
    public static void main(String[] args) {
        Button b = new Button(&quot;start&quot;);
        b.addActionListener(new EventHandler());
}

class EventHandler implements ActionLisener{
    public void actionPerformed(ActionEvent e) {
        System.out.println(&quot;ActionEvent occurred!&quot;)
    }
}

// 익명 클래스로 변환
class Inner{
    public static void main(String[] args) {
        Button b = new Button(&quot;start&quot;);
        b.addActionListener(new ActionLisener(){ // 익명 클래스로 대체
                public void actionPerformed(ActionEvent e) {
                    System.out.println(&quot;ActionEvent occurred!&quot;)
                }
            }
        );
}</code></pre>
<br/>

<hr>
<h3 id="『-핵심-정리-』-3">『 핵심 정리 』</h3>
<ul>
<li><p>내부 클래스는 어떤 클래스 내부에 선언된 클래스이다.</p>
</li>
<li><p>종류는 선언위치에 따른 변수의 종류와 유사하다.</p>
</li>
<li><p>외부와 내부 클래스는 서로 긴밀한 관계로 연결된 작업을 처리하기 위해 사용한다.</p>
</li>
<li><p>내부 클래스 사용 시, 외부 클래스 멤버에 쉽게 접근할 수 있고 동시에 밖으로 유출되지 않기 때문에 코드의 복잡성이 낮아진다.</p>
</li>
<li><p>상수를 제외한 static멤버는 static 내부 클래스에만 정의할 수 있다.</p>
</li>
<li><p>생성 시점에 따른 인스턴스 멤버와 static멤버의 관계는 내부 클래스끼리 or 내부-외부 클래스 관계에서도 유사하다.</p>
</li>
<li><p>익명 클래스는 일회용 클래스로, 상속과 구현 둘 중 하나만 가능하며 객체도 한 개만 만들 수 있다.</p>
</li>
</ul>
<br/>

<hr>
<blockquote>
<p>도움이 되셨다면 <strong>&#39;좋아요&#39;</strong> 부탁드립니다 :)</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[Ch07. 객체지향 프로그래밍Ⅲ]]></title>
            <link>https://velog.io/@ho_c/Ch07.-%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/@ho_c/Ch07.-%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>Tue, 09 May 2023 01:48:52 GMT</pubDate>
            <description><![CDATA[<h1 id="들어가는-말">들어가는 말</h1>
<ul>
<li><p>이전 파트는 OOP의 기본 단위인 &#39;클래스&#39;를 알아봤다면, 
이번 파트는 클래스의 세팅과 각 클래스 간의 관계를 만드는 법에 대해 배운다.</p>
</li>
<li><p>OOP의 꽃은 &#39;다형성&#39;이다. 이번 주제 중 가장 중요한 것으로 꼭 이해하고 넘어가야 한다.</p>
</li>
<li><p>다형성과 추상을 통해서 우리는 하나의 &#39;규격&#39;을 만들고, 이를 구현함으로서 OOP의 본질을 살려낼 수 있다.</p>
</li>
<li><p>따라서 인터페이스는 꼭 이해해야 된다.</p>
</li>
</ul>
<hr>
<h2 id="1-상속inheritance">1. 상속(inheritance)</h2>
<blockquote>
<p><strong>상속은 기존 클래스를 재사용해, 새 클래스를 만드는 것</strong>
<strong>예) 설계도 재탕</strong></p>
</blockquote>
<ul>
<li><p>코드 재사용성이 본질 같지만 이는 반절만 정답이다. 상속의 나머지 본질은 조상 클래스에 정의된 코드를 확장시키는 것이다.</p>
</li>
<li><p>기존 작성된 코드를 재사용하기 때문에, 적은 코드로 새 클래스를 만들 수 있다.</p>
</li>
<li><p>상속 관계 안의 코드는 공통적으로 관리할 수 있다.</p>
</li>
<li><p>프로그램의 &#39;유지 보수&#39;, &#39;확장&#39;의 면에서 중요하다.</p>
</li>
</ul>
<br/>

<h3 id="상속-관계">상속 관계</h3>
<blockquote>
<ul>
<li>생성자와 초기화 블럭은 상속되지 않는다.</li>
</ul>
</blockquote>
<ul>
<li>자손 클래스의 멤버수는 조상보다 같거나 많다.</li>
<li>자손 간의 연결은 되지 않는다.</li>
</ul>
<br/>

<p><strong>상속  구현</strong></p>
<pre><code class="language-java">class Parent {}
class Child extends Parent{
        // 내부 구현...
}</code></pre>
<h4 id="정의">정의</h4>
<ul>
<li><p>상속은 <code>extends</code>을 사용해서 상속 관계를 구현한다.</p>
</li>
<li><p>조상 클래스 : 상속하는 클래스(Parent)</p>
</li>
<li><p>자손 클래스 : 상속받는 클래스(Child)</p>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/ho_c/post/a1ec42cf-4c72-41a2-aa85-5ab6d99af39d/image.jpg" alt=""></p>
<ul>
<li>위와 같은 사진은 상속계층도라고 한다. </li>
</ul>
<br/>

<p><strong>조상 클래스의 멤버 상속</strong></p>
<blockquote>
<p>상속 관계 안에서 자손 클래스는 조상 클래스의 &#39;모든 멤버(변수, 메서드)&#39;를 상속받는다.
즉, 조상에 정의된 코드가 자동으로 따라온다.</p>
</blockquote>
<pre><code class="language-java">class Parent {
    int age;
}
class Child extends Parent{}


/*-----실행 코드-----*/
    public static void main(String[] args) {
        Child c = new Child();
        c.age = 10;

        System.out.println(c.age); // 10
    }</code></pre>
<p><img src="https://velog.velcdn.com/images/ho_c/post/d51cf363-65e7-4914-bff5-1e12ec213806/image.jpg" alt=""></p>
<ul>
<li><p>Parent의 멤버변수 <code>age</code>는 존재하고, 이를 상속받는 Child 내에도 존재한다.</p>
</li>
<li><p>물론 코드 상에서는 Child에 적혀있진 않다. 그 이유는 사진처럼 두 클래스의 관계에서 Parent가 부분집합이기 때문이다.
그래서 실제 Child인스턴스를 생성하면, 내부에 <code>age</code>라는 인스턴스 변수를 제어할 수 있다.</p>
</li>
</ul>
<br/>

<p>** 자손 클래스의 멤버 추가**</p>
<blockquote>
<p>이번엔 반대로 자손 클래스에 멤버를 추가해보았다.</p>
</blockquote>
<pre><code class="language-java">class Parent {
    int age;
}
class Child extends Parent{
    void play(){
        System.out.println(&quot;play&quot;);
    }
}


/*-----실행 코드-----*/
    public static void main(String[] args) {
        Parent p = new Parent();
        p.play(); // 에러
    }</code></pre>
<p><img src="https://velog.velcdn.com/images/ho_c/post/6a1f79f0-844d-4444-b853-00384417920f/image.jpg" alt=""></p>
<ul>
<li>반대로 자손 클래스에 멤버가 추가된 경우에는 조상 클래스에 아무런 영향이 없다.</li>
<li>즉, 조상 클래스의 변화만 자손 클래스에 영향을 준다.
그렇기 때문에 Parent의 인스턴스에선 <code>play()</code>라는 메서드를 사용할 수 없는 것이다.</li>
</ul>
<br/>

<blockquote>
<p>따라서 자손 클래스는 <strong>조상 클래스보다 멤버가 같거나 더 많다.</strong> 상속을 받을 수록 멤버들이 늘어나기 때문이다. 그런 점에서 상속을 구현할 때, <strong>extends(확장)</strong>이라 쓰는 것이다.</p>
</blockquote>
<br/>

<p><strong>자손 클래스 간의 관계</strong></p>
<blockquote>
<p><strong>Parent를 상속받는 자손 클래스를 하나 더 만들어보자.</strong></p>
</blockquote>
<pre><code class="language-java">// 조상
class Parent { /*...이전 예제 참조*/ }

// 자손1
class Child1 extends Parent{
    /*...이전 예제 참조*/
}

// 자손2
class Child2 extends Parent{
    /*...생략*/
}</code></pre>
<ul>
<li>Child1, Child2는 Parent를 공통적으로 상속받는다. 하지만 둘의 연관성은 Parent 하나일 뿐 서로 연결되지 않는다. </li>
</ul>
<p><img src="https://velog.velcdn.com/images/ho_c/post/e432c94a-fcb4-4f25-a962-05490ff3c55a/image.jpg" alt=""></p>
<ul>
<li>따라서 두 클래스에서 공통적으로 쓰고 싶은 것은 Parent에 추가하는게 더 유용하다.
이렇게 사용하면 중복되는 코드를 줄이고, 한 곳에서 관리할 수 있다는 장점이 있다.</li>
</ul>
<br/>

<p><strong>자손의 자손 클래스</strong></p>
<blockquote>
<p><strong>그렇다면 자손을 상속받는 자손 클래스는 어떨까?</strong></p>
</blockquote>
<pre><code class="language-java">// 조상
class Parent { /*...이전 예제 참조*/ }

// 자손1
class Child extends Parent{
    /*...이전 예제 참조*/
}

// 자손의 자손
class GrandChild extends Child{
    /*...생략*/
}</code></pre>
<p><img src="https://velog.velcdn.com/images/ho_c/post/ef61dce9-ea88-497c-8eec-fd6f39efe18a/image.jpg" alt=""></p>
<ul>
<li>자손은 생성자, 초기화를 제외한 모든 멤버를 물려받는다. 
따라서 GrandChild 역시 Parent의 멤버들을 물려받았다. 사실상 더 Parent에서 더 확장된 클래스가 된 것이다.</li>
</ul>
<br/>

<hr>
<h4 id="정리">정리</h4>
<ul>
<li><p>클래스 간 중복된 코드를 최소화하여, 한 군데(조상)에서 관리할 수 있다.</p>
</li>
<li><p>이 경우 조상의 멤버를 변경하면 상속받는 자손도 모두 바뀌기 때문에 유지보수에 있어 안정적이다. </p>
</li>
<li><p>자손 클래스는 자기 배부에 정의된 멤버들만 관리하면 되서 코드가 줄어들고, 관리가 쉬워진다.</p>
</li>
<li><p>제일 중요한 건, <strong>자손 인스턴스는 내부에 조상 인스턴스도 함께 생성되어 합쳐진 인스턴스이다..</strong>
그래서 조상 인스턴스를 따로 생성하지 않고, 멤버들을 사용할 수 있다.</p>
</li>
</ul>
<hr>
<h3 id="포함-관계">포함 관계</h3>
<blockquote>
<p><strong>포함관계는 상속을 사용하지 않고 클래스 간의 관계를 만들어주는 방법이다.</strong></p>
</blockquote>
<pre><code class="language-java">class Circle {
    int x;
    int y;
    int raidus;
}</code></pre>
<blockquote>
<p>위와 같은 원을 표현하는 클래스가 있다고 가정하고, 저 안의 좌표값만 따로 클래스로 빼서 사용해보자.</p>
</blockquote>
<pre><code class="language-java">class Circle {
    Point p = new Point();
    int raidus;
}

class Point{
    int x;
    int y
}</code></pre>
<ul>
<li><p>포함관계는 한 클래스의 멤버변수로 다른 클래스의 참조변수를 선언하는 것이다.</p>
</li>
<li><p>포함관계를 이용하면 좌표값처럼, 단위별로 묶어서 클래스를 만들어 멤버변수를 관리할 수 있다.</p>
</li>
<li><p>역으로 단위별로 묶인 클래스들은 다른 클래스에서 필요 시 포함관계로 재사용할 수 있다.</p>
</li>
<li><p>단, 참조되는 클래스의 변경 시 참조하는 클래스의 코드도 수정해야된다.</p>
</li>
</ul>
<br/>

<hr>
<p><strong>클래스 간 관계 결정 Tip</strong></p>
<blockquote>
<p>실제 코드 자체는 별반 다른 게 없다. 참조해서 쓸건지, 상속받아 쓸건지 그 차이일 뿐이다.</p>
</blockquote>
<pre><code class="language-java">// 포함
class Circle {
    Point c = new Point();
    int r;
}

// 상속
class Circle extends Point {
    int r;
}

class Point{
    int x;
    int y
}</code></pre>
<blockquote>
<p>그럼 어떻게 구분해야될까?
<strong>상속과 포함을 결정하는 방식은 &#39;문장&#39;으로 만들어 보면 쉽다.</strong></p>
</blockquote>
<ul>
<li>상속 관계 : &#39;~은 ~이다.&#39;(is-a) / 곰은 동물이다.</li>
<li>포함 관계 : &#39;~은 ~을 가지고 있다&#39;(has-a) / 자동차는 바퀴를 갖고 있다.</li>
</ul>
<pre><code class="language-java">// 도형
class Shape { /*생략*/}

// 점
class Point { /*생략*/}

// 원
class Circle extends Shape { // &#39;원은 도형이다&#39; ▷ 상속관계

    // &#39;원은 점이 있다&#39; ▷ 포함관계
    Point p = new Point(); 
}</code></pre>
<br/>

<hr>
<h3 id="단일-상속">단일 상속</h3>
<ul>
<li><p>C++에서는 여러 조상 클래스를 상속받는 &#39;다중 상속&#39;이 가능하다. 하지만 자바는 이를 막았다.</p>
</li>
<li><p>그래서 자바에선 &#39;단일 상속&#39;만 가능하다.
다중 상속이 가능한 건 인터페이스...</p>
</li>
</ul>
<blockquote>
<p><strong>그럼 다중 상속이 뭐가 좋은걸까?</strong></p>
</blockquote>
<ul>
<li><p>다중상속이 가능하면, 여러 가지 기능을 가진 클래스들을 상속받아서 복합적인 클래스를 쉽게 만들어 낼 수 있다.</p>
</li>
<li><p>근데 자바가 이를 막은 건, 그 단점인 <strong>&#39;동명의 인스턴스 멤버&#39;를 구별할 방법이 없다는 것이다.</strong>
따라서 자바는 중복된 이름에 의한 충돌을 막기 위해서 &#39;단일 상속&#39;만 허용한다.</p>
</li>
<li><p>그나마 가능하려면, 오버로딩이나 메서드명 변경이 필요할텐데 이를 사용하는 것도 비효율적이다. 그래서 자바는 코드의 신뢰성을 높이기 위해 &#39;단일 상속&#39;을 선택한다.</p>
</li>
</ul>
<blockquote>
<p>꼼수로 상속-포함 관계를 이용해 다중 상속을 구현하는 방법도 있다.</p>
</blockquote>
<pre><code class="language-java">class Tv{
    boolean power;
    int channel;

    void power(){ power = !power; };
    void channelUp(){ ++channel; };
    void channelDown(){ --channel; };
}

class VCR{
    booelean power; 
    int counter = 0;
    void power(){ power = !power; }
    void play(){ /*구현*/ };
    void stop(){ /*구현*/ };
    void rew(){ /*구현*/ };
    void ff() { /*구현*/ };
}

class TVCR extends Tv{ // 단일상속
    VCR vcr = new VCR();

    void play(){
        vcr.play();
    }

    // ...
}</code></pre>
<ul>
<li><p>Tv의 멤버는 상속받아서 사용하고, VCR의 멤버 메서드들은 포함관계를 통해 TVCR의 실제 메서드로 실행되게 한다.</p>
</li>
<li><p>VCR의 메서드를 변경하면, TVCR의 메서드도 함께 변경되어 상속의 효과를 낼 수 있다.</p>
</li>
</ul>
<br/>

<hr>
<h3 id="object클래스">Object클래스</h3>
<blockquote>
<p>Object 클래스는 모든 클래스의 조상 클래스다.
즉, 모든 클래스는 Object클래스를 자동 상속받는다.</p>
</blockquote>
<ul>
<li><p>Object의 멤버들을 모든 클래스들이 사용할 수 있다.
<code>toString(), equals(Object o)</code></p>
</li>
<li><p>Object o의 다형성 때문에 equals()가 가능하다.</p>
</li>
</ul>
<br/>

<hr>
<h3 id="『-핵심-정리-』">『 핵심 정리 』</h3>
<ul>
<li><p>자손은 조상 클래스의 멤버만 상속받는다. (생성자, 초기화 X)</p>
</li>
<li><p>자손 인스턴스가 생성되면 내부에 조상 인스턴스도 생성된다.
그래서 자손 안에서 부모의 멤버를 사용할 수 있다.</p>
</li>
<li><p>상속이 늘어날 수록 &#39;기능이 확장된다.&#39; (extends)</p>
</li>
<li><p>포함관계는 다른 클래스의 참조변수를 멤버 변수로 사용해서 구현한다.</p>
</li>
<li><p>자손 클래스에서 부모 클래스의 메서드를 오버라이딩(재정의)할 수 있다.
그러면 부모 클래스의 메서드가 아닌, 자손 클래스의 멤버 메서드로 실행된다.</p>
</li>
<li><p>다중 상속은 관계 안에서 동명의 멤버를 구별하기 어렵기 때문에, 자바는 단일 상속만 허용한다.</p>
</li>
<li><p>다중 상속은 상속과 포함을 적절히 사용해서 자바에서 구현할 수는 있다.</p>
</li>
<li><p>모든 클래스의 조상은 &#39;Object&#39;클래스이다. 컴파일러가 자동으로 추가해준다.
물론 다른 클래스에서 상속받게 작성되면, 추가하지 않는다.</p>
</li>
</ul>
<br/>

<hr>
<h2 id="2-오버라이딩overriding">2. 오버라이딩(overriding)</h2>
<h3 id="정의-1">정의</h3>
<blockquote>
<p>조상 클래스로부터 상속받은 메서드를 재정의하는 것</p>
</blockquote>
<ul>
<li>상속 받은 메서드 자체는 그대로 쓰기도 하지만, 자손 클래스의 상황에 따라 변경하기도 한다.</li>
</ul>
<pre><code class="language-java">class Point{
    int x;
    int y;

    String getLocation(){
        return &quot;x :&quot; + x + &quot;, y :&quot; + y;
    }
}

class Point3D extends Point{
    int z;
    // 오버라이딩
    String getLocation(){
        return &quot;x :&quot; + x + &quot;, y :&quot; + y + &quot;, z :&quot; + z;
    }
}</code></pre>
<blockquote>
<p>Point클래스는 x,y 2차원 좌표만 <code>getLocation()</code>으로 출력한다. 
하지만 상속받은 Point3D클래스는 3차원이기 때문에 오버라이딩되야 한다.</p>
</blockquote>
<br/>

<h3 id="조건">조건</h3>
<blockquote>
<p>오버라이딩은 메서드의 구현부만 자손 클래스의 새로 작성하는 것이다.
따라서 <strong>선언부(메서드명, 반환타입, 매개변수)</strong>는 조상 클래스와 일치해야 한다.</p>
</blockquote>
<ul>
<li>JDK 1.5부터는 반환타입은 자손 클래스의 타입으로 변경하는게 가능해졌다.</li>
</ul>
<blockquote>
<p><strong>선언부 외에도 &#39;접근 제어자, 예외&#39;는 제한 조건 안에서 변경이 가능하다.</strong></p>
</blockquote>
<ol>
<li><p><strong>접근 제어자는 조상 클래스의 메서드보다 좁으면 안된다.</strong></p>
<ul>
<li><p>접근제어자의 순서는 <code>public, protected, (default), private</code> 로 뒤로 갈수록 좁아진다.</p>
</li>
<li><p>예를 들어, 조상이 public이면 자손도 public메서드여야 한다는 것이다.</p>
</li>
<li><p>반대로 조상이 private면 자손은 &#39;public, protected, (default), private&#39; 모두 가능하다.</p>
</li>
</ul>
</li>
</ol>
<br/>

<ol start="2">
<li><p><strong>조상 클래스의 메서드보다 예외의 범위, 개수가 크면 안 된다.</strong></p>
<ul>
<li><p>에러가 발생하면, main()에서 코드 진행이 튕겨나가고 프로그램이 멈춘다.</p>
</li>
<li><p>이 에러를 처리하는 것을 예외 처리라고 한다. 이 중 <code>throws 예외처리 클래스명</code>를 사용하는 것을 &#39;예외 전가&#39;라 한다.</p>
</li>
<li><p>예외 전가는 특정 에러를 감지하고 처리하는 기능을 구현한 &#39;클래스&#39;들을 사용한다.</p>
</li>
<li><p>이 클래스의 수가 조상 보다 많으면 안된다.</p>
</li>
<li><p>주의해야될 건, 무조건 수가 적다고 끝나는 게 아니다. 예외마다 담당하는 범위가 있는데
클래스의 조상이 Object인 것처럼, 예외의 조상은 Exception 클래스이다.</p>
</li>
<li><p>Exception은 예외의 범위가 가장 넓음으로, 조상의 예외 범위가 이보다 작을 경우 자손에서 사용할 수 없다.</p>
<br/>


</li>
</ul>
</li>
</ol>
<ol start="3">
<li><strong>인스턴스 메서드, static 메서드 간의 상호 변경은 불가능하다.</strong></li>
</ol>
<blockquote>
<p>정적 메서드는 자손 클래스에서 똑같은 이름으로 만들 순 있다.
<strong>다만 이 경우는 &#39;오버라이딩&#39;이 아니다.</strong> 왜냐면 정적 메서드는 정의된 클래스에 묶여 있기 때문이다.</p>
</blockquote>
<br/>

<hr>
<h4 id="오버로딩-vs-오버라이딩">오버로딩 vs 오버라이딩</h4>
<blockquote>
<p>이름이 비슷할 뿐, 목적이 다르다.</p>
</blockquote>
<ul>
<li>오버로딩 : 기존에 없는 새로운 메서드를 정의하는 것
(이름만 같을 뿐, 매개변수, 구현, 반환타입은 모두 다르다.)</li>
</ul>
<br/>

<ul>
<li>오버라이딩 : 상속받은 메서드의 내용을 변경하는 것
(자손 클래스에서 기능을 확장하는 용도)</li>
</ul>
<hr>
<h3 id="super"><code>super</code></h3>
<blockquote>
<p>멤버변수와 지역변수의 이름이 같을 떄, <code>this</code>를 사용한 것처럼 <code>super</code>는 상속받은 멤버와 자신의 멤버를 구분하는 지역변수이다.</p>
</blockquote>
<ul>
<li><p>조상 클래스(슈퍼 클래스)와 자손 클래스의 멤버를 구분하는 용도.</p>
</li>
<li><p>super는 조상 클래스의 멤버를 가리킨다.</p>
</li>
<li><p>물론 내부적으로 자손의 멤버이기 때문에 this도 사용 가능하다.</p>
</li>
<li><p>그래서 상속 관계에서 중복 정의된 멤버는 super로 구분한다.</p>
</li>
<li><p><code>super</code>에는 조상 인스턴스의 주소가 저장되며, <code>this</code>와 함께 스택 프레임에 자동 저장되는 참조변수이다.</p>
</li>
<li><p><code>super</code> 역시, 인스턴스 메서드에서만 사용이 가능하다. (클래스 메서드에선 불가)</p>
</li>
</ul>
<br/>

<h4 id="멤버-변수에-사용되는-super">멤버 변수에 사용되는 <code>super</code></h4>
<pre><code class="language-java">// 조상
class Parent {
    int x = 10;
}

// 자손
class Child extends Parent {
    int x = 20;

    void method(){
        System.out.println(&quot;x= &quot; + x);
        System.out.println(&quot;this.x= &quot; + this.x);
        System.out.println(&quot;super.x= &quot; + super.x);
    }
}

// 실행
class Test {
    public static void main(String[] args){
        Child c = new Child();
        c.method();
    }
}

/*결과
x= 10
this.x= 20
super.x= 10
*/</code></pre>
<blockquote>
<p><code>super</code>로 구분이 가능하기 때문에, 조상에 선언된 멤버 변수는 자손에서 중복 선언이 가능하다.</p>
</blockquote>
<br/>

<h4 id="오버라이딩된-메서드에-사용되는-super">오버라이딩된 메서드에 사용되는 <code>super</code></h4>
<ul>
<li>조상의 메서드도 <code>super</code>가 있으면, 구분이 가능해서 자손 메서드의 내부에서도 호출이 가능하다.</li>
<li>자손 인스턴스가 생성되면, 조상 인스턴스도 함께 생성된다는 걸 잊지 말자.</li>
</ul>
<pre><code class="language-java">// 조상
class Point {
    int x;
    int y;

     String getLocation(){
         return &quot;x : &quot; + x + &quot;, y : &quot; + y;
     }
}

// 자손
class Point3D extends Point {
    int z;
    // 오버라이딩
    String getLocation(){
         return super.getLocation() + &quot;, z : &quot; + z;
    }
}

// 실행
class Test {
    public static void main(String[] args){
        Child c = new Child();
        c.method();
    }
}

/*결과
x= 10
this.x= 20
super.x= 10
*/</code></pre>
<blockquote>
<p>조상 메서드의 변경은 자손의 메서드에 자동으로 반영되니, 유지보수에 유용하다. </p>
</blockquote>
<br/>

<hr>
<h3 id="super-1"><code>super()</code></h3>
<blockquote>
<p><strong>조상의 생성자</strong></p>
</blockquote>
<ul>
<li><code>this()</code> : <strong>같은 클래스의 다른 생성자를 호출</strong></li>
<li><code>super()</code> : <strong>조상 클래스의 생성자를 호출</strong></li>
</ul>
<br/>

<p>** 1. 생성자를 쓰는 이유 **</p>
<ul>
<li><p>자바의 객체는 초기화하지 않으면 생성할 수 없다.<br>그래서 생성자는 클래스에 한 개 이상 정의되야 한다.</p>
</li>
<li><p>또한, 생성자 없이는 인스턴스 생성 시, 외부에서 매개변수로 인스턴스 변수의 값을 넘겨줄 방법이 없다.</p>
</li>
</ul>
<br/>


<p>** 2. super()의 필요성 **</p>
<ul>
<li><p>인스턴스 변수의 초기화는 해당 클래스가 처리해야 한다. 
그래서 초기화블럭과 생성자는 상속되지 않는다.</p>
</li>
<li><p>상속에선 자손 인스턴스가 생성되면, 내부에 조상 인스턴스도 함께 생성된다.</p>
</li>
<li><p>따라서 자손에서 사용되는 조상의 멤버는 조상의 생성자로 처리해야 한다.</p>
</li>
</ul>
<br/>

<p>** 3. 생성자의 추가 조건 **</p>
<blockquote>
<p>*<em>모든 클래스 생성자의 첫 줄에는 반드시 생성자를 호출해야 한다. *</em></p>
</blockquote>
<ul>
<li><p>모든 클래스의 조상은 Object클래스이다. 즉, 모든 클래스는 숨겨진 상속관계가 있다.
이는 컴파일러가 자동 추가해준다.</p>
</li>
<li><p>따라서 모든 클래스는 <code>super()</code>으로 자신의 조상 Object의 생성자를 호출한다. 
이 <code>super()</code> 호출은 Object()까지 호출해야 끝난다.</p>
</li>
<li><p><code>super()</code>는 각 생성자의 첫 줄에서 호출된다. 크게 두 가지 이유가 있다.</p>
<ul>
<li>자손 멤버가 조상의 멤버를 사용할 수 있어서</li>
<li>조상 인스턴스의 생성을 항상 보장하기 위해</li>
</ul>
</li>
</ul>
<ul>
<li><p>중복되는 생성자 간에는 <code>this()</code>를 사용해 한 생성자에서만 <code>super()</code>를 호출하는 방법도 있다.</p>
</li>
<li><p>반대로 각 생성자가 중복되지 않을 땐, 생성자마다 <code>super()</code>를 넣어줘야 한다.</p>
</li>
<li><p>다행히 <code>super()</code>를 안 써도, 컴파일러가 생성자의 첫 줄에 <code>super()</code>를 자동 삽입한다.</p>
</li>
</ul>
<pre><code class="language-java">class Point {
    int x;
    int y;

    Point(){
        this(0, 10); // 다른 생성자 호출 → 조건 충족
    }

    Point(int x, int y){
        this.x = x; // ← 첫 줄에 생성자가 없음으로 컴파일러가 super(); 자동 추가
        this.y = y;
    }
}</code></pre>
<br/>


<blockquote>
<p><strong>주의 : 조상에 기본 생성자가 없는 경우, 따로 추가하지 않으면 컴파일 에러가 발생한다.</strong></p>
</blockquote>
<p>** 4. 조상의 기본생성자가 없는 경우 **</p>
<pre><code class="language-java">// 조상
class Point { // 컴파일러가 extends Object를 추가한다.
    int x = 10;
    int y = 20;

    // 따로 기본생성자 point()를 정의하지 않았다.

    // 매개변수가 있는 생성자
    Point(int x, int y){
        // 컴파일러가 자동으로 super(); 추가한다. super()는 Object();을 의미
        this.x = x;
        this.y = y;
    }
}

// 자손
class Point3D extends Point {
    int z = 30;

    Point3D() {
        this(100, 200, 300); // 다른 생성자를 호출한다. super();가 없음
    }

    Point3D(int x, int y, int z) {
        super(x, y); // 여기서 조상 클래스의 생성자를 호출한다.
        this.z = z;
    }

    /*컴파일 에러 예시
    Point3D(int x, int y, int z) {
        super(); ◀ 따로 안적어도 컴파일러가 super();를 자동 삽입. 하지만 Point 클래스에 기본 생성자가 없기 때문에 컴파일에러가 난다.
        this.z = z;
    }
    */
}

// 실행
class Test {
    Point3D p3 = new Point3D();
}
/* 생성자 호출 순서 
Point3D(); → Point3D(int x, int y, int z); → Point(int x, int y) → Object();
*/</code></pre>
<ul>
<li>만약 조상 클래스에 매개변수가 있는 생성자만 정의해서, 기본 생성자가 없을 땐 <code>super()</code>를 호출하면 컴파일 오류가 발생한다. </li>
</ul>
<ul>
<li>이 경우엔 조상 생성자 맞춰 매개변수를 넣어 호출하든지, 조상에 기본 생성자를 정의해서 해결한다.</li>
</ul>
<br/>

<hr>
<h3 id="『-핵심-정리-』-1">『 핵심 정리 』</h3>
<ul>
<li><p>오버라이딩은 상속받은 메서드를 재정의하는 것으로, 구현을 강제하거나 기능을 확장하는 용도로 쓰인다.</p>
</li>
<li><p>오버라이딩은 선언부가 일치해야 한다. (반환타입이 자손 클래스까지는 허용)</p>
</li>
<li><p>오버라이딩 시, 접근제어자와 예외 처리의 조건을 맞춰야 한다.</p>
</li>
<li><p>클래스 멤버는 자신이 정의된 클래스에 묶여 있다. 따라서 오버라이딩을 구현할 수 없다.</p>
</li>
<li><p><code>super</code> 참조변수는 조상과 자손의 중복을 구분할 때 사용한다. (this와 같음)</p>
</li>
<li><p>인스턴스 메서드 안에서 인스턴스 멤버를 대상으로 사용이 가능하다.</p>
</li>
<li><p>자손에선 생성자의 첫 줄엔 부모의 생성자 <code>super()</code>를 호출해야 한다.</p>
</li>
<li><p>컴파일러가 기본생성자를 컴파일 시, 추가한다.
컴파일러는 사용자 생성자가 하나라도 정의되면 기본 생성자를 추가하지 않는다.
그래서 조상 클래스에 기본 생성자가 없으면 컴파일 에러가 난다. </p>
</li>
</ul>
<br/>

<hr>
<h2 id="3-package-import">3. package, import</h2>
<br/>

<h3 id="package란">package란?</h3>
<blockquote>
<p>패키지는 클래스의 묶음이다.</p>
</blockquote>
<ul>
<li><p>패키지에는 클래스, 인터페이스를 포함할 수 있다.</p>
</li>
<li><p>서로 관련된 클래스끼리 그룹 단위로 묶어서 관리할 수 있다.</p>
</li>
<li><p>같은 이름의 클래스도 패키지가 다르면 존재할 수 있다.
그래서 클래스명의 풀네임에는 패키지가 포함된다. ex) java.lang.String</p>
</li>
<li><p>클래스(.class)가 물리적 파일인 것처럼 패키지도 물리적인 하나의 디렉토리다.
즉, 패키지는 클래스파일을 포함하는 &#39;디렉토리&#39;다.</p>
</li>
<li><p>패키지도 하위 패키지를 생성할 수 있다.
패키지와 패키지는 <code>.</code>을 구분자로 하여 계층 구조를 구성할 수 있다.</p>
</li>
</ul>
<br/>

<h3 id="패키지-선언">패키지 선언</h3>
<blockquote>
<p>하나의 소스파일에는 첫 문장으로 단 한 번의 패키지 선언만을 허용한다.</p>
</blockquote>
<pre><code class="language-java">package 패키지명;</code></pre>
<ul>
<li><p>패키지명은 대소문자 모두 가능하지만, 클래스와 구분하기 위해 소문자가 원칙이다.</p>
</li>
<li><p>해당 소스파일에 포함된 모든 클래스나 인터페이스는 선언된 패키지에 속한다.</p>
</li>
<li><p>패키지 선언을 안하면 기본 제공인 <strong>&#39;이름없는 패키지&#39;</strong>에 속한다.</p>
</li>
</ul>
<br/>

<p><strong>1. 패키지 생성</strong></p>
<pre><code class="language-java">package pack1.pack2.pack3; // 패키지 선언

class PackageTest {
    /*구현*/
}
</code></pre>
<ul>
<li><p>예제는 pack1, pack2, pack3 총 3개의 패키지가 생성되고, pack3 안에 PackageTest 클래스가 생성되는 걸 의도한다.</p>
</li>
<li><p>이제 CMD에서 <code>-d</code> 옵션을 걸어, 해당 파일을 컴파일하면 선언된 패키지가 존재하지 않아도 만들어준다.</p>
</li>
</ul>
<pre><code>C:\OpenJDK\jdk-11.0.1\work&gt;javac -d . PackageTest.java</code></pre><ul>
<li><code>-d</code> 옵션 뒤에는 루트 디렉토리, 최상단 디렉토리의 위치를 선정해줄 수 있다.
예제는 현재 폴더 <code>C:\OpenJDK\jdk-11.0.1\work</code>가 루트 디렉토리로 설정.</li>
</ul>
<br/>

<p><strong>2. 클래스패스</strong></p>
<blockquote>
<p>클래스패스(classpath)는 컴파일러, JVM 등이 클래스의 위치를 찾는데 사용되는 경로</p>
</blockquote>
<ul>
<li><p>위 예제에서 루트 디렉토리는 <code>C:\OpenJDK\jdk-11.0.1\work</code>이다.
이걸 클래스패스에 포함시켜줘야, JVM이 PackageTest 클래스를 찾을 수 있다.</p>
</li>
<li><p><strong>클래스패스 지정법 : 시스템 변수 설정</strong></p>
<ul>
<li><p>경로 : <strong>제어판→시스템→고급 시스템 설정→환경변수-시스템or사용자 변수-새로 만들기</strong></p>
</li>
<li><p>변수 이름 : CLASSPATH</p>
</li>
<li><p>변수 값 : C:\OpenJDK\jdk-11.0.1\work</p>
</li>
<li><p>사용자 변수 : 로그인된 계정 별로 설정된 변수 (지역변수)</p>
</li>
<li><p>시스템 변수 : 시스템 전체에서 사용되는 변수 (전역변수)</p>
</li>
<li><p><code>;</code> : 구분자 → 변수 값을 구별해서, 여러 개 넣어줄 수 있음</p>
</li>
<li><p><code>.;</code> : 클래스패스를 따로 설정할 경우, 기본값이었던 현재 디렉토리가 사라짐. 
그래서 현재 디렉토리를 의미하는 <code>.</code>도 추가해야됨.</p>
</li>
<li><p><code>.jar</code> 파일은 클래스패스에 추가하려면, 파일명까지 적어줘야 된다.</p>
</li>
</ul>
</li>
</ul>
<ul>
<li>실행 시, 클래스의 패키지명(경로)까지 적어줘야 한다.
<code>&gt;java package1.package2.package3.PackageTest</code></li>
</ul>
<ul>
<li>기본 클래스패스를 사용하면, 클래스패스 지정없이 아래 경로에다가 넣어서 실행이 가능하다.<ul>
<li>클래스 : JDK설치디렉토리/jre/classes → 따로 만들어야 됨.</li>
<li>jar파일 : JDK설치디렉토리/jre/lib/ext </li>
</ul>
</li>
</ul>
<ul>
<li>일시적으로는 <code>-cp 사용할 클래스패스 패키지경로.클래스명</code> 옵션을 사용하면 된다.</li>
</ul>
<br/>

<hr>
<h3 id="import문">import문</h3>
<blockquote>
<p>컴파일러에게 소스파일에 사용된 클래스의 패키지 정보를 제공해서 패키지명을 생략</p>
</blockquote>
<ul>
<li><p>다른 패키지의 클래스를 사용하려면 패키지명까지 포함한 풀네임으로 클래스명을 적어야 된다.</p>
</li>
<li><p>import문으로 미리 패키지를 선언하면, 패키지명은 생략할 수 있다.</p>
</li>
<li><p>import문에 선언된 패키지를 컴파일러가 컴파일하면서 클래스명 앞에 붙여 준다.</p>
</li>
<li><p>같은 패키지 내의 클래스 간에는 생략이 가능하다.</p>
</li>
</ul>
<br/>

<h4 id="import문-선언">import문 선언</h4>
<ul>
<li><p><strong>선언 위치 : package문과 class 선언문 사이</strong></p>
<p> package문
 import문
 public class 클래스명 {}</p>
</li>
</ul>
<ul>
<li><p><strong>선언 방법</strong></p>
<p> import 패키지명.클래스명;
 import 패키지명.*; </p>
<ul>
<li><p><code>*</code>는 지정된 패키지의 모든 클래스를 의미한다. 
이건 클래스를 의미하는 거지, 패키지가 아니라는 것만 주의하자.</p>
</li>
<li><p><code>import.java.*</code> ◀ 이렇게 쓰는 거 아니다.</p>
</li>
</ul>
</li>
</ul>
<br/>

<h4 id="static-import">static import</h4>
<blockquote>
<p>static import문을 사용하면, static멤버를 호출할 때 클래스명을 생략할 수 있다.</p>
</blockquote>
<pre><code class="language-java">import static java.lang.Integer.*; // Integer클래스의 모든 static 메서드
import static java.lang.Math.random; // Math.random()만. 괄호 안붙임.
import static java.lang.System.out; // System.out out 으로 참조가능


System.out.println(Math.random()); → out.println(random());</code></pre>
<br/>

<hr>
<h3 id="『-핵심-정리-』-2">『 핵심 정리 』</h3>
<ul>
<li><p>모든 클래스는 하나의 패키지 안에 속해있어야 한다.</p>
</li>
<li><p>패키지는 클래스 파일을 갖고 있는 디렉토리다.</p>
</li>
<li><p>동명의 클래스도 패키지가 다르면 가능하다.</p>
</li>
<li><p>클래스패스를 사용자 설정하면, 자동으로 현재 디렉토리를 사용하지 못한다.</p>
</li>
<li><p>기본 경로를 사용하면 지정 경로에 파일을 넣어주기만 하면 된다.</p>
</li>
<li><p>import문이 있으면, 다른 패키지의 클래스를 호출 시 패키지명을 생략할 수 있다.</p>
</li>
</ul>
<br/>

<hr>
<h2 id="4-제어자modifier">4. 제어자(modifier)</h2>
<h3 id="정의-2">정의</h3>
<blockquote>
<p>클래스, 변수, 메서드 선언부에 함께 사용해서 부가적인 의미를 부여하는 예약어</p>
</blockquote>
<br/>

<p><strong>종류</strong></p>
<ul>
<li>접근 제어자 : <strong>public / protect / (default) / private</strong></li>
<li>그 외 : <strong>static / final / abstract / native / transient / synchronized / volatile / strictfp</strong></li>
</ul>
<p>▶  접근 제어자는 한개만 가능, 나머지는 하나의 대상에 대해 여러 제어자를 조합해서 사용 가능 </p>
<hr>
<h3 id="static">static</h3>
<blockquote>
<p>&#39;클래스의&#39;, &#39;공통적인&#39; → 인스턴스를 생성하지 않고 사용이 가능해진다.</p>
</blockquote>
<ul>
<li><p>앞에서 배운 것처럼, 클래스 멤버들은 인스턴스 간 공통된 값을 유지하는게 목적이다.</p>
</li>
<li><p>또는 인스턴스 멤버를 사용하지 않아서, 굳이 인스턴스 생성과정을 거칠 필요가 없다.</p>
</li>
<li><p>이러한 경우 <code>static</code>을 멤버필드, 메서드, 초기화 블럭에 사용한다.</p>
</li>
</ul>
<br/>

<p>*<em><code>static</code> + 멤버변수 *</em></p>
<ul>
<li>모든 인스턴스에서 공통된 값을 유지하게 된다.</li>
<li>인스턴스 생성 없기 사용 가능</li>
<li>클래스가 메모리 로딩 시, 생성</li>
</ul>
<br/>

<p>*<em><code>static</code> + 메서드 *</em></p>
<ul>
<li>인스턴스 생성 없이 호출이 가능하다.</li>
<li>static 메서드는 인스턴스 멤버를 사용할 수 없다. (생성 시점 차이)</li>
</ul>
<br/>

<p>*<em><code>static</code> + 초기화블럭{} *</em></p>
<ul>
<li>static 변수를 초기화는 용도로 주로 사용.</li>
<li>클래스가 메모리 로딩 시, 수행</li>
</ul>
<br/>

<hr>
<h3 id="final">final</h3>
<blockquote>
<p>&#39;마지막의&#39;, &#39;변경될 수 없는&#39; → 상수</p>
</blockquote>
<ul>
<li>사용된 메서드, 클래스, 변수가 변경이 불가능하게 하는 것이 목적이다.</li>
<li>거의 모든 곳에 사용할 수 있다.</li>
</ul>
<br/>

<p>*<em><code>final</code> + 클래스 *</em></p>
<ul>
<li>변경 불가, 확장(상속)될 수 없는 클래스가 된다.</li>
<li>다른 클래스의 조상이 될 수 없다.</li>
<li>String, Math가 그 예시, 굳이 변경할 필요도 없다.</li>
</ul>
<br/>

<p>*<em><code>final</code> + 메서드 *</em></p>
<ul>
<li>변경 불가능한 메서드, 오버라이딩을 할 수 없다.</li>
</ul>
<br/>

<p>*<em><code>final</code> + 변수 *</em></p>
<ul>
<li>지역, 멤버든 상관없이 &#39;상수&#39;가 된다.</li>
</ul>
<br/>


<p>*<em>생성자를 이용한 final멤버 변수 초기화 *</em></p>
<ul>
<li><p>상수는 선언, 초기화를 같이 해서 변경을 막지만, 멤버변수인 경우 예외가 있다.</p>
</li>
<li><p>클래스 내부엔 선언, 생성자로 초기화하면 final 멤버변수도 초기화가 가능하다.</p>
</li>
<li><p>이렇게 하면 인스턴스마다 다른 값의 final 멤버변수를 가질 수 있다.</p>
</li>
<li><p>불가능하면 용도가 static 변수랑 별반 다를게 없다.</p>
</li>
</ul>
<pre><code class="language-java">class Card{
    final int NUMBER;
    final String KIND;

    Card(int num, String kind){
        NUMBER = num;
        KIND  = kind;
    }
}</code></pre>
<br/>

<hr>
<h3 id="abstract">abstract</h3>
<blockquote>
<p>&#39;미완성&#39; → 완성되지 않아서, 사용할 수가 없다.</p>
</blockquote>
<ul>
<li><p><code>abstract</code> 를 붙인 메서드, 클래스는 사용할 수 없다.
정확히는 사용보단, 호출과 생성이 불가하다.</p>
</li>
<li><p>그래서 추상 메서드가 들어간 클래스는 &#39;추상 클래스&#39;가 되어야 한다.</p>
</li>
</ul>
<br/>

<p><strong><code>abstract</code> + 클래스</strong></p>
<ul>
<li>클래스 내에 추상 메서드가 선언되어 인스턴스 생성이 불가하다.</li>
</ul>
<br/>

<p><strong><code>abstract</code> + 메서드</strong></p>
<ul>
<li>선언부만 작성된 추상 메서드, 구현부는 작성되지 않았다.</li>
</ul>
<blockquote>
<p>일반 메서드의 방향은 기능의 &#39;확장&#39;이다.
이런 점에서 추상 메서드는 상속받는 쪽에서 해당 메서드를 &#39;구현&#39;하길 강요하는 용도로
사용할 수 있다.</p>
</blockquote>
<br/>

<hr>
<h3 id="접근-제어자">접근 제어자</h3>
<blockquote>
<p><strong>멤버, 클래스에 사용해 외부(패키지, 클래스)로부터의 &#39;접근&#39;을 제한한다.</strong></p>
</blockquote>
<br/>

<p><strong>1. 종류</strong></p>
<ul>
<li><strong>public, protected, default, private</strong></li>
</ul>
<br/>

<p><strong>2. 사용 위치</strong></p>
<ul>
<li><p><strong>클래스, 멤버변수, 메서드, 생성자</strong></p>
</li>
<li><p>지역변수에는 못 쓴다.</p>
</li>
</ul>
<table>
<thead>
<tr>
<th align="center">대상</th>
<th align="center">사용 가능한 접근제어자</th>
</tr>
</thead>
<tbody><tr>
<td align="center">클래스</td>
<td align="center">public, (default)</td>
</tr>
<tr>
<td align="center">멤버</td>
<td align="center">public, protected, (default), private</td>
</tr>
</tbody></table>
<br/>

<p><strong>3. 접근 범위</strong></p>
<table>
<thead>
<tr>
<th align="center">제어자</th>
<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>
<td align="center">어느 곳에서든 접근할 수 있다.</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>
<td align="center">상속 관계 한정. 패키지 관계없이 접근 가능</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>
<td align="center">기본값. 같은 패키지 안에서만 접근 가능</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>
<td align="center">해당 클래스 내에서만 가능, 내부에 접근하려면 메서드를 사용해야됨.</td>
</tr>
</tbody></table>
<br/>

<p>*<em>캡슐화 *</em></p>
<blockquote>
<p>OOP에선 &#39;데이터 보호&#39;를 위해 캡슐화라는 방식을 사용하며, 이를 구현하기 위해 접근 제어자하를 사용한다.</p>
</blockquote>
<ul>
<li><p>캡슐화 자체는 데이터를 숨기는 것이다. 알약처럼 말이다.</p>
</li>
<li><p>캡슐화를 통해, 사용자는 클래스 내부 데이터에 맘대로 접근할 수 없다.</p>
</li>
<li><p>사용자에게 노출되는 정보 제한해, 과잉 정보로 인한 오판단을 방지한다. </p>
<blockquote>
<p>리모컨 설명서에 리모컨의 모든 로직을 작성하면, 제대로 쓸 수 있는 사람이 몇명이나 될까?
즉, 외부에서 접근할 필요 없는 멤버를 숨김으로 복잡성을 줄일 수 있다.</p>
</blockquote>
</li>
<li><p>위와 연결되어 내부에서만 쓰는 건 외부에서 접근 못하게 막는다.</p>
</li>
<li><p>유출하면 안되는 중요 로직들을 숨길 수 있다.</p>
</li>
<li><p>접근 범위에 따라서 오류 수정 시, 그 범위를 유추할 수 있다.
<code>private</code>는 그 해당 멤버만 보면 된다. (자손 클래스에서도 접근이 불가함)</p>
</li>
</ul>
<br/>

<p><strong>Getter&amp;Setter</strong></p>
<blockquote>
<p>private로 제한된 멤버는 어떻게 외부로 어떻게 전달할까?</p>
</blockquote>
<ul>
<li><p>이를 위해 사용되는 것이 Getter, Setter형식의 메서드이다.</p>
</li>
<li><p>Getter : 외부로 값을 반환 /  Setter : 내부의 값을 변경</p>
</li>
</ul>
<pre><code class="language-java">class PvClass {
    private int A;

    // Getter
    public int getA(){ return A; }

    // Setter
    public void setA(int A) {
        this.A = A;
    }

}

class PbClass {
    public static void main(String[] args){
        PvClass pv = new pvClass();

        // Setter 호출
        pv.setA(10);

        // Getter 호출
        System.out.println(pv.getA()); // 10;
    }
}</code></pre>
<br/>

<p> <strong>생성자의 접근 제어자</strong></p>
<blockquote>
<p>Sigleton패턴이라 불리며, 인스턴스의 생성 개수를 접근 제어자를 통해서 제한할 수 있다.</p>
</blockquote>
<ul>
<li><p>외부에서 제한없이 인스턴스 생성이 가능하면, 같은 용도의 인스턴스가 동시다발적으로 생성되어 메모리가 낭비된다.</p>
</li>
<li><p>특히 DB관련 작업에서는 DB연결이 끊기는 상황도 발생한다. </p>
</li>
</ul>
<blockquote>
<p><strong>그래서 싱글톤은 어떻게 구현하느냐?</strong></p>
</blockquote>
<p><strong>1. 내부에서 인스턴스를 생성한다.</strong></p>
<pre><code class="language-java">class Singleton{
    private static Singleton instance = new Sigleton();    
}</code></pre>
<ul>
<li><p>생성과 초기화를 외부에서 하면 애초에 싱글톤이 성립이 안된다.
따라서 내부에서 클래스가 로딩되면서 만들어지게 한다.</p>
</li>
<li><p>클래스가 로딩되면서, 힙 영역에 인스턴스가 생성되고 해당 인스턴스는 메서드 영역의 클래스 변수를 통해 접근이 가능하다.</p>
</li>
<li><p>인스턴스는 계속 연결되어 있기 때문에 사라지지 않는다.</p>
</li>
</ul>
<br/>

<p><strong>2. 생성자는 private로 접근을 제한한다.</strong></p>
<pre><code class="language-java">class Singleton{
    private static Singleton instance = new Sigleton();    

    private Sigleton(){
        /.../
    } 
}</code></pre>
<br/>

<p><strong>3. Getter로 인스턴스의 참조변수를 외부로 반환한다.</strong></p>
<pre><code class="language-java">class Singleton{
    private static Singleton instance = new Sigleton();    

    private Sigleton(){
        /.../
    } 

    // Getter
    public static Sigleton getInstance(){
        return Sigleton
    }
}

class SingletonTest{
    public static void main(String[] args){
        // Getter 호출
        Singleton singleInstance = Singleton.getInstance();
    }
}</code></pre>
<br/>

<hr>
<h3 id="제어자-조합">제어자 조합</h3>
<h4 id="제어자와-대상-정리"><strong>제어자와 대상 정리</strong></h4>
<table>
<thead>
<tr>
<th align="center">대상</th>
<th align="center">사용 가능한 접근제어자</th>
</tr>
</thead>
<tbody><tr>
<td align="center">클래스</td>
<td align="center">public, (default), abstract, final</td>
</tr>
<tr>
<td align="center">메서드</td>
<td align="center">public, protected, (default), private, abstract, final, static</td>
</tr>
<tr>
<td align="center">멤버변수</td>
<td align="center">public, protected, (default), private, final, static</td>
</tr>
<tr>
<td align="center">지역변수</td>
<td align="center">final</td>
</tr>
</tbody></table>
<br/>

<h4 id="주의-사항">주의 사항</h4>
<p><strong>1. 클래스에 final, abstract은 함께 사용할 수 없다.</strong></p>
<ul>
<li>상속해서 구현해야될 클래스(abstract)를 상속불가(final)로 만들면, 모순이라 안된다.</li>
</ul>
<br/>

<p><strong>2. 메서드에 static, abstract은 함께 사용할 수 없다.</strong></p>
<ul>
<li>static 메서드는 인스턴스 생성없이 사용할 용도. 미완성된(구현부가 없는) 메서드는 필요가 없다.</li>
</ul>
<br/>

<p><strong>3. 추상메서드(abstract)의 접근 제어자는 private면 안된다.</strong></p>
<ul>
<li>private는 같은 클래스 안에서만 접근할 수 있다. 그게 외부에서 접근할 수 없으면 자손에서 어떻게 오버라이딩 해줄 수 있을까.</li>
</ul>
<br/>

<p><strong>4. 메서드의 private, final은 중복될 필요가 없다.</strong></p>
<ul>
<li>final도 자손에서 변경 불가, private는 상속 불가. 결국 유도하는 기능은 같다.</li>
</ul>
<br/>


<hr>
<h3 id="『-핵심-정리-』-3">『 핵심 정리 』</h3>
<ul>
<li><p>제어자는 조합해서 사용이 가능하다.</p>
</li>
<li><p>final이 붙은 인스턴스 변수는 생성자로 초기화가 가능하다.</p>
</li>
<li><p><strong>생성자가 <code>private</code>로 제한된 클래스는 상속할 수 없다.</strong>
부모 생성자가 외부에서 호출이 불가능하기 때문이다.
이 경우 클래스도 <code>final</code>로 상속이 불가능하다는 걸 알려주는게 좋다.</p>
</li>
<li><p>접근 제한자는 OOP의 데이터를 보호하기 위해서 사용된다.</p>
</li>
<li><p>추상화된 메서드와 클래스는 상속관계에서 기능의 확장과 구현이 목적이다.</p>
</li>
</ul>
<br/>

<hr>
<blockquote>
<p>도움이 되셨다면 <strong>&#39;좋아요&#39;</strong> 부탁드립니다 :)</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[Ch06. 객체지향 프로그래밍Ⅱ]]></title>
            <link>https://velog.io/@ho_c/Ch06.-%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-dop06ims</link>
            <guid>https://velog.io/@ho_c/Ch06.-%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-dop06ims</guid>
            <pubDate>Wed, 03 May 2023 07:51:47 GMT</pubDate>
            <description><![CDATA[<h1 id="들어가는-말">들어가는 말</h1>
<blockquote>
<p>변수와 메서드는 클래스의 기본 요소이다. 이번 시간엔  이 둘이 객체지향적으론 어떻게 응용되는지 알아보자.</p>
</blockquote>
<hr>
<h2 id="1-오버로딩overloading">1. 오버로딩(Overloading)</h2>
<blockquote>
<p>한 클래스 내의 동명의 메서드를 여러 개 정의하는 것.</p>
</blockquote>
<ul>
<li><p>메서드의 기능은 같지만 사용자가 입력하는 인자가 다른 경우가 있다.</p>
</li>
<li><p>예를 들면 <code>println()</code> 가 있다. 사용할 때는 한 가지 메서드 같지만 실제로는 매개변수가 서로 다른 동명의 메서드들이 있다.</p>
</li>
<li><p>즉, 오버로딩은 같은 기능을 하지만, 사용하는 인자가 다를 경우 적용한다.</p>
</li>
</ul>
<br/>

<h4 id="조건">조건</h4>
<blockquote>
<p>이름만 같다고 해서 오버로딩이 아니다. 다음 두 조건을 충족해야된다.</p>
</blockquote>
<ol>
<li><p>메서드의 이름이 같아야 됨.</p>
</li>
<li><p>매개 변수의 개수 or 타입이 달라야됨.</p>
</li>
</ol>
<p><strong>* 반환타입은 영향을 미치지 못한다.</strong></p>
<br/>

<h4 id="예시">예시</h4>
<p><strong>1. <code>println()</code></strong></p>
<pre><code class="language-java">void println()
void println(boolean x)
void println(char x)
void println(char[] x)
void println(double x)
void println(float x)
void println(int x)
void println(long x)
void println(Object x)
void println(String x)</code></pre>
<ul>
<li>실제 정의된 <code>println()</code>의 오버로딩 유형이다. 
각자 매개변수가 달라서 사용자가 넣는 인자에 따라 그에 맞는 메서드가 선택되어 실행된다.</li>
</ul>
<br/>

<p><strong>2. 매개변수명은 다르지만, 타입이 같은 경우</strong></p>
<pre><code class="language-java">int add(int a, int b){ return 1 }
int add(int x, int y){ return 1 }</code></pre>
<ul>
<li>이 경우, 매개변수명만 다를 뿐이지 타입과 개수가 일치한다. 따라서 오버로딩이 적용되지 않는다.</li>
</ul>
<br/>

<p><strong>3. 리턴 타입만 다른 경우</strong></p>
<pre><code class="language-java">int add(int a, int b){ return 1 }
long add(int a, int b{ return 1 }</code></pre>
<ul>
<li>1번과 같이 반환타입만 다를 뿐 조건에 충족하지 않아, 오버로딩이 아니다.</li>
</ul>
<br/>

<p><strong>4. 매개변수의 순서가 다른 경우</strong></p>
<pre><code class="language-java">long add(int a, long b){ return 1 }
long add(long a, int b){ return 1 }</code></pre>
<ul>
<li>매개변수의 순서로 메서드 구분이 가능해, 오버로딩이 적용된다.
다만, 리터럴을 정확히 구분해서 넘겨줘야 한다. (산술변환 x)
예를 들어 <code>(3, 3)</code>을 넘기면 타입 불일치로 메서드 구분이 불가능해 컴파일 에러가 난다.</li>
</ul>
<br/>

<h4 id="장점">장점</h4>
<p><strong><code>println()</code>을 보면, 장점은 뚜렷하다.</strong></p>
<ul>
<li>여러 메서드를 하나의 이름으로 묶어서 사용할 수 있다.</li>
<li>그래서 한 이름으로 여러 메서드의 기능을 유추할 수 있다.</li>
<li>메서드 이름을 절약할 수 있다.</li>
</ul>
<br/>

<hr>
<h3 id="가변인자">가변인자</h3>
<blockquote>
<p><code>메서드명(타입... 변수명);</code></p>
</blockquote>
<ul>
<li><p>기존 메서드를 정의할 때, 매개변수의 수는 고정적이다. (유동적으로 조절 x)</p>
</li>
<li><p>막상, 상황에 따라 매개변수의 수가 다르거나 아예 정할 수 없을 수도 있다. 
그러면 모든 상황에 맞게 메서드를 오버로딩을 해야된다.</p>
</li>
<li><p>그래서 호출 시점에 매개변수를 동적으로 제어하는 기능인 &#39;가변인자&#39;가 만들어졌다.
말 그대로 매개변수가 몇개나 들어올지 모른다.</p>
</li>
</ul>
<br/>

<h4 id="조건-1">조건</h4>
<blockquote>
<p>가변인자는 다른 매개변수들과의 구분이 어렵다,.
그래서 무조건 매개변수의 마지막 순서에 넣어야 된다.</p>
</blockquote>
<pre><code class="language-java">// 1. 구분 가능
public static int add(int a, int... b){return 0};
/* add(3,5,6,7);
순서에 따라 3은 무조건 a의 값이 된다.
*/

// 2. 구분 불가
public static int add(int... a, int b){return 0};
/* add(3,5,6,7);
7이 가변인자인지, b의 값인지 구분할 수 없다.
때문에 가변인자가 있는 경우 오버로딩된 메서드끼리 구분이 불가능하기도 하다.
*/</code></pre>
<br/>

<h4 id="예시-1">예시</h4>
<p>*<em>1. 매개변수 타입이 같은 오버로딩 메서드를 하나로 대체할 수 있다. *</em></p>
<pre><code class="language-java">// 오버로딩
String concatenate(String s1, String s2) { ... }
String concatenate(String s1, String s2, String s3) { ... }
String concatenate(String s1, String s2, String s3, String s4){ ... }

// 가변인자
String concatenate(String... s) { ... }</code></pre>
<br/>

<p><strong>2. 호출 시, 인자의 개수가 가변적이다.</strong></p>
<pre><code class="language-java">concatenate();
concatenate(&quot;a&quot;);
concatenate(new String[]{&quot;A&quot;, &quot;B&quot;};</code></pre>
<ul>
<li>인자는 없을 수도 있고, 배열도 가능하다.</li>
</ul>
<br/>

<p><strong>3. 가변인자는 내부적으로 배열을 사용한다.</strong></p>
<ul>
<li>가변인자가 선언된 메서드는 호출마다 가변인자를 배열에 넣어 넘긴다.</li>
<li>이는 매개변수를 배열로 하는 것과 차이가 있다.
매개변수가 배열이면, 인자의 생략이 불가능하다. (null, 0인 배열을 넘겨야됨)</li>
</ul>
<pre><code class="language-java">String concatenate1(String... s1) { ... } // 가변인자
String concatenate2(String[] s2) { ... } // 배열

public void static main(String[] args){
        concatenate1(); // 인자 생략 가능
        concatenate2(); // 인자 생략 불가
}</code></pre>
<br/>

<p><strong>4. 가변인자가 있을 땐, 오버로딩을 안하는 게 좋다.</strong></p>
<pre><code class="language-java">int add(int a, int... b){ ... };
int add(int... b) { ... };</code></pre>
<ul>
<li><p>분명 오버로딩과 가변인자의 사용조건에 부합하지만, 컴파일 에러가 난다.
그건 컴퓨터가 오버로딩된 메서드들을 구분할 수 없기 때문이다.</p>
</li>
<li><p>1를 인자로 넘긴다 할 때, 그게 <code>a</code>의 값인지, 가변인자 <code>b</code>인지 구별할 수 없다. 
왜냐면 가변인자는 인자의 생략도 가능하기 때문이다.</p>
</li>
</ul>
<br/>

<hr>
<h2 id="2-생성자constructor">2. 생성자(Constructor)</h2>
<blockquote>
<p>생성자는 인스턴스 생성 시, 정해둔 작업을 수행하는 메서드이다.
주로 인스턴스 변수의 초기화를 위해 사용된다.</p>
</blockquote>
<pre><code class="language-java">클래스이름(타입 변수명, 타입 변수명, ...) {
    // 인스턴스 생성시 수행될 코드
}</code></pre>
<br/>

<h4 id="조건-2">조건</h4>
<blockquote>
<p>다음 조건을 충족하면 클래스의 생성자가 된다.</p>
</blockquote>
<ol>
<li><p>클래스 내에 선언된다.</p>
</li>
<li><p>리턴값은 없다.</p>
</li>
<li><p>생성자의 이름은 클래스의 이름과 같아야 한다.</p>
</li>
<li><p>생성자의 오버로딩도 가능하다.</p>
</li>
</ol>
<pre><code class="language-java">class Card {
    // 기본 생성자
    Card() {}

    // 매개변수가 있는 생성자
    Card(String k, int num){
        ...
    }
}</code></pre>
<br/>

<p><strong>생성자 수행 과정</strong></p>
<pre><code class="language-java">public void static main(String[] args){
    Card c = new Card();
}</code></pre>
<ol>
<li>연산자 new에 의해 heap에 Card클래스의 인스턴스 생성</li>
<li>생성자 <code>Card()</code> 호출</li>
<li>new의 결과로 인스턴스의 주소가 반환되어, 참조변수 c에 저장</li>
</ol>
<br/>


<h3 id="기본-생성자">기본 생성자</h3>
<blockquote>
<p>모든 클래스에는 반드시 하나 이상의 생성자가 정의되어야 한다.</p>
</blockquote>
<ul>
<li><p>기본으로 컴파일러가 &#39;기본 생성자&#39;를 추가해주기 때문에, 따로 정의할 필요는 없다.</p>
</li>
<li><p>단, 컴파일러의 생성자 추가는 <strong>&#39;생성자가 하나도 정의되지 않은 클래스&#39;</strong>에만 적용된다.</p>
</li>
<li><p>변수의 초기화작업이 없는 경우에는 기본 생성자만 써도 괜찮다.</p>
</li>
<li><p>접근제어자가 <code>public</code> 이면 기본 생성자로 <code>public 클래스명(){}</code> 이 추가된다.</p>
</li>
</ul>
<pre><code class="language-java">class Card {
    // 기본 생성자
    Card(){ }
}</code></pre>
<br/>

<p><strong>사용자 생성자</strong></p>
<blockquote>
<p>사용자가 생성자를 정의한 경우, 컴파일러는 기본생성자를 추가하지 않는다.</p>
</blockquote>
<pre><code class="language-java">class Deck1{
    int card = 20;
}

class Deck2{
    int card;
    // 사용자 생성자
    Deck2(int card){
        this.card = card; 
    }
}
/*-------------------------------------*/
public void static main(String[] args){
    Deck1 d1 = new Deck1();
    Deck2 d2 = new Deck2(); //  컴파일 에러
}</code></pre>
<ul>
<li><p>Deck1은 기본생성자를 컴파일러가 추가해준다.
반면 Deck2는 사용자 생성자가 있기 때문에 기본생성자가 존재하지 않는다.</p>
</li>
<li><p>main() 실행 시, Deck2에는 일치하는 생성자가 없기 때문에 컴파일에러가 발생한다.
해결하려면, 기본생성자를 정의하든지, 사용자 생성자에 맞춰서 매개변수를 전달해야 한다.</p>
</li>
</ul>
<br/>

<h3 id="매개변수가-있는-생성자">매개변수가 있는 생성자</h3>
<blockquote>
<p>생성자도 매개변수를 선언해서 호출 시 값을 넘겨받을 수 있다.</p>
</blockquote>
<pre><code class="language-java">class Deck2{
    int card;
    Deck2(int card){
        this.card = card; 
    }
}
/*-------------------------------------*/
public void static main(String[] args){
    Deck2 d2 = new Deck2(20);
    System.out.println(d2.card); // 20
}</code></pre>
<ul>
<li>생성자를 사용해 초기화를 하면, 인스턴스 생성 후 하나씩 초기화하지 않아도 된다.</li>
</ul>
<br/>

<hr>
<h3 id="this-this"><code>this(), this</code></h3>
<blockquote>
<p><code>this</code>는 클래스 내에서 중복되는 생성자나 변수명을 구별하기 위해 사용한다.</p>
</blockquote>
<br/>

<h4 id="this--생성자-내에서-다른-생성자를-호출하는-조건"><code>this()</code> : 생성자 내에서 다른 생성자를 호출하는 조건</h4>
<blockquote>
</blockquote>
<ol>
<li>생성자 이름으로 클래스명 대신 <code>this</code>를 사용한다.</li>
<li>한 생성자에서 다른 생성자를 호출할 때는, 첫 줄에서만 가능하다.</li>
</ol>
<ul>
<li><p>위 두 조건을 만족해야지 에러 없이 생성자 간 호출이 가능하다.</p>
</li>
<li><p>모든 생성자를 <code>this()</code>로 호출하기에, 매개변수를 기반으로 생성자를 구별한다.</p>
</li>
<li><p>초기화 작업 중 다른 생성자를 호출하면 기존 초기화 작업이 무의미해질 수 있기 때문에 첫째 줄에서만 호출이 가능하다.</p>
</li>
</ul>
<pre><code class="language-java">class Car{
    String color;
    String gearType;
    int door;

    // 생성자1
    Car(){
        this(&quot;white&quot;, &quot;auto&quot;, 4); // 생성자3 호출
    }

    // 생성자2
    Car(String color){
        this(color, &quot;auto&quot;, 4); // 생성자3 호출
    }

    // 생성자3
    Car(String color, String gearType, int door) {
        this.color= color;
        this.gearType = gearType;
        this.door = door;
    }
}</code></pre>
<ul>
<li>여기서 생성자3은 <code>this</code>를 통해, 동명의 인스턴스변수와 매개변수를 구분한다.</li>
</ul>
<br/>

<h4 id="참조변수-this">참조변수 <code>this</code></h4>
<ul>
<li><p><code>this</code>는 참조변수로 인스턴스 자신의 주소를 가리킨다. 이때, this()와는 다른 개념이다.</p>
</li>
<li><p><code>this</code>로 접근할 수 있는 건 &#39;인스턴스 멤버&#39;만 가능하다.
클래스 멤버는 생성 시점이 달라 인스턴스가 없을 수도 있기 때문이다.</p>
</li>
</ul>
<ul>
<li>인스턴스 메서드안에 <code>this</code>가 지역변수로 숨겨진 채 존재한다.
이 말은 인스턴스 메서드의 &#39;스택 프레임&#39; 내의 지역변수 공간에 this가 저장되는 걸 말한다.</li>
</ul>
<ul>
<li>따라서 <code>this</code>는 인스턴스 메서드 안에서만 사용이 가능하다.</li>
</ul>
<blockquote>
<p>* <strong>Stack-frame</strong> : 메서드 내의 매개변수와 지역변수, 메서드의 반환 주소 등을 저장하는 공간</p>
</blockquote>
<br/>

<hr>
<h3 id="생성자를-이용한-인스턴스-복사">생성자를 이용한 인스턴스 복사</h3>
<blockquote>
<p>사용 중인 인스턴스와 같은 상태의 인스턴스를 더 만들 때, 생성자를 이용할 수 있다.</p>
</blockquote>
<ul>
<li><p>두 인스턴스의 주소는 다르다. 전혀 다른 인스턴스지만, 갖고 있는 상태(변수)가 일치한다.</p>
</li>
<li><p>방법은 두 가지이다. 1. 생성자를 이용해서 / 2. <code>clone()</code> 사용.</p>
</li>
</ul>
<br/>

<h4 id="생성자-이용">생성자 이용</h4>
<pre><code class="language-java">Car(Car c){
    color = c.color;
    gearType = c.gearType;
    door = c.door;
} 
// 막상하면, this 참조변수 쓰는 거랑 별반 다르지 않다.</code></pre>
<blockquote>
<p><strong>결과적으로 인스턴스를 생성할 때는 2가지를 고려해야 한다.</strong></p>
</blockquote>
<ul>
<li>클래스 : 어떤 클래스의 인스턴스를 만들 것인가?</li>
<li>생성자 : 선택한 클래스의 어떤 생성자를 사용해서 인스턴스를 만들 것인가?</li>
</ul>
<br/>

<hr>
<h2 id="3-변수의-초기화">3. 변수의 초기화</h2>
<h3 id="초기화">초기화</h3>
<blockquote>
<p>변수를 선언하고 처음 값을 저장하는 걸 &#39;초기화&#39;라고 한다.</p>
</blockquote>
<ul>
<li>멤버 변수 : 초기화를 안해도 자료형의 기본값으로 자동 초기화 된다.</li>
<li>지역 변수 : 초기화를 안하면 쓸 수가 없다.</li>
</ul>
<pre><code class="language-java">class InitTest {
    int x; // 인스턴스 변수, 0으로 초기화
    float y; // 인스턴스 변수, 0.0f로 초기화

    void method1(){
        int i; // 지역변수
        int j = i; // 에러, i는 초기화하지 않아서 사용 불가
    }
}</code></pre>
<blockquote>
<p>따라서 멤버변수, 배열(참조변수 null)의 초기화는 선택 / 지역 변수는 초기화 필수</p>
</blockquote>
<br/>


<h3 id="멤버변수의-초기화">멤버변수의 초기화</h3>
<blockquote>
<p>지역변수는 그 자리에서 초기화를 해야되지만, 멤버변수는 방식은 &#39;명시적 초기화, 생성자, 초기화 블록&#39;으로 여러 가지이다.</p>
</blockquote>
<h4 id="1-명시적-초기화">1) 명시적 초기화</h4>
<blockquote>
<p>변수 선언과 초기화를 동시에 하는 것.</p>
</blockquote>
<pre><code class="language-java">class Car{
    int door = 4; // 기본형 변수 초기화
    Engine e = new Engine(); // 참조형 변수 초기화

    //...
}</code></pre>
<ul>
<li>가장 간단하지만, 변수 초기화 값이 정해져 있다.</li>
</ul>
<br/>

<h4 id="2-초기화-블록">2) 초기화 블록</h4>
<blockquote>
<p>초기화 블럭은 두 가지 종류가 있고, 각 종류의 변수의 초기화에 사용된다.</p>
</blockquote>
<ul>
<li>클래스 초기화 블럭 : 클래스 변수의 초기화</li>
<li>인스턴스 초기화 블럭 : 인스턴스 변수의 초기화 (static 키워드만 붙이면 됨)</li>
</ul>
<br/>

<pre><code class="language-java">class InitBlock{
    static { /* 클래스 초기화 블럭 */}

    { /* 인스턴스 초기화 블럭 */}
}</code></pre>
<ul>
<li>초기화 블럭을 사용하면, 내부에서 제어문을 사용할 수 있다.
더불어 복잡한 초기화가 가능해진다.</li>
</ul>
<ul>
<li><p>클래스 초기화 블럭은 클래스가 메모리에 첫 로딩될 때 한번만 수행된다.</p>
</li>
<li><p>반대로 인스턴스는 생성자처럼 인스턴스 생성마다 수행된다.
이때, 인스턴스 초기화 블럭이 생성자보다 먼저 실행된다.</p>
</li>
</ul>
<pre><code class="language-java">// 실행 순서를 응용해서 초기화 과정을 더 편하게 만들 수 있다.
Car(){
    count++; // 중복
    serialNo = count; // 중복
    color =&quot;White&quot;;
    gearType =&quot;Auto&quot;;
}

Car(String color, String gearType){
    count++; // 중복
    serialNo =count; // 중복
    this.color = color;
    this.gearType = gearType;
}

/* -----------  코드 개선  ----------- */
// 인스턴스 블럭 → 중복 코드를 하나로 처리
{
    count++;
    serialNo = count;
}

Car(){
    color =&quot;White&quot;;
    gearType =&quot;Auto&quot;;
}

Car(String color, String gearType){
    this.color = color;
    this.gearType = gearType;
}</code></pre>
<br/>

<h3 id="초기화-시기와-순서">초기화 시기와 순서</h3>
<blockquote>
<p>초기화의 수행 시기와 순서를 파악하는 것은 매우 중요하다.</p>
</blockquote>
<p>** 초기화 시점 **</p>
<ul>
<li>클래스 변수 : 클래스가 메모리에 처음 로딩(선언, 생성)될 때, 단 한번 초기화</li>
<li>인스턴스 변수 : 인스턴스가 생성될 때마다 인스턴스별 초기화</li>
</ul>
<br/>

<p>** 초기화 순서 **</p>
<ul>
<li>클래스 변수 : 기본값 → 명시적 초기화 → 클래스 초기화 블럭</li>
<li>인스턴스 변수 : 기본값 → 명시적 초기화 → 인스턴스 초기화 블럭 → 생성자</li>
</ul>
<br/>

<h4 id="정리">정리</h4>
<ul>
<li><p>클래스 변수의 초기화는 클래스 데이터가 method area에 올라올 때, 진행된다.</p>
</li>
<li><p>한 번 클래스 데이터가 로딩되었다면, 재로딩되지 않는다.</p>
</li>
<li><p>클래스 데이터 로딩은 JVM의 종류마다 다르게 설계되어 있다.</p>
</li>
<li><p>우선 순위에 따라서 초기화 값이 변화한다.</p>
</li>
<li><p>인스턴스 변수는 &#39;생성자&#39;가 가장 마지막에 실행된다.</p>
</li>
<li><p>공통된 부분은 &#39;초기화 블럭&#39;으로 처리해주는 것이 효율적이고, 객체지향적이다.
중복을 제거하고, 한 곳에서 코드를 관리하기 때문에.</p>
</li>
</ul>
<hr>
<blockquote>
<p>도움이 되셨다면 <strong>&#39;좋아요&#39;</strong> 부탁드립니다 :)</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[Ch06. 객체지향 프로그래밍Ⅰ]]></title>
            <link>https://velog.io/@ho_c/Ch06.-%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/@ho_c/Ch06.-%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, 13 Apr 2023 09:43:35 GMT</pubDate>
            <description><![CDATA[<h1 id="들어가는-말">들어가는 말</h1>
<ul>
<li><p>내가 생각하는 객체지향프로그래밍의 핵심은 프로그램을 <strong>&#39;메모리 상의 기계&#39;</strong>로 구현한다는 것이다. </p>
</li>
<li><p>먼저 절차적 언어는 프로그램을 코드의 순차적 실행으로 만들었었다. 물론 조건과 반복이 있지만 결국 프로그램은 위에서 아래로 실행한다. 
때문에 함수 단위로 관리하더라도, 중복되는 코드와 이에 대한 수정은 비효율적 방식으로 해결된다.</p>
</li>
<li><p>따라서 효율적 해결을 위해 객체지향은 <strong>&#39;변수(속성)와 메서드(기능)의 집합&#39;</strong>, 곧 클래스라는 <strong>&#39;부품 설계도&#39;</strong>를 만든다. 그리고 각 부품 간의 관계를 설정해, 프로그램 실행과 함께 부품들이 자동 조립되어 <strong>&#39;기계&#39;</strong>를 구현하게 한다. </p>
</li>
<li><p>결과적으로 <strong>기계(프로그램)</strong>의 문제가 발생할 시, 부품만 교체해주면 된다. 
즉, 클래스를 고치거나 변경하면 된다.</p>
</li>
</ul>
<hr>
<h2 id="1-객체지향언어">1. 객체지향언어</h2>
<blockquote>
<p>객체지향언어는 절차적 언어에 규칙을 추가해, 유기적인 프로그램을 구현한 것으로 <strong>‘프로그램 유지보수’</strong> 측면에서 큰 이점을 갖는다.</p>
</blockquote>
<p><strong>① 코드의 재사용성이 높다.</strong></p>
<ul>
<li><p>필요한 기능이 있으면, 그 기능이 포함된 클래스를 만들거나 원래 있는 클래스를 메모리에 로딩해서 언제든지 사용할 수 있다.</p>
</li>
<li><p>클래스 자체도 기존 클래스를 상속받아 확장해서 만들 수 있다.</p>
</li>
</ul>
<br/>

<p><strong>② 코드의 관리가 용이하다.</strong></p>
<ul>
<li><p>클래스 단위로 코드가 묶여있어, 수정할 대상이 포함된 클래스의 코드만 수정하면 된다.</p>
</li>
<li><p>특히, 다형성을 이용하면 조상의 참조변수로 자손의 인스턴스를 사용할 수 있다. 
그래서 필요할 때, 인스턴스만 교체해주면 된다.</p>
</li>
</ul>
<br/>

<p><strong>③ 신뢰성이 높은 프로그래밍이 가능하다.</strong></p>
<ul>
<li><p>제어자나 메서드를 이용해서 외부에서 클래스 내부에 데이터에 접근하는 것은 개발자가 제어할 수 있다.</p>
</li>
<li><p>그래서 들어오는 값을 확인한 뒤, 기능을 처리하거나 아예 사용자에게 필요하지 않는 데이터를 제한함으로써 클래스의 오남용을 방지할 수 있다.</p>
</li>
</ul>
<hr>
<h2 id="2-클래스와-객체">2. 클래스와 객체</h2>
<h3 id="클래스와-객체의-정의와-용도">클래스와 객체의 정의와 용도</h3>
<blockquote>
</blockquote>
<p>클래스 : 객체를 정의한 설계도
용도 : 메모리에 로딩하여 실제 사용할 ‘객체’를 만드는 데 사용</p>
<ul>
<li><p><strong>클래스 자체는 설계도이다. **
프로그램상에서 우리는 클래스를 사용하는 것이 아니라 클래스를 기반으로 만들어낸 부품인 **‘인스턴스’</strong>를 사용한다.</p>
</li>
<li><p>설계도인 클래스만 잘 정의하면, 필요할 때 인스턴스로 만들어서 사용하면 된다는 이점이 있다.</p>
</li>
<li><p>JDK는 그래서 프로그래밍을 위한 기본 설계도(JAVA API)를 제공한다.</p>
</li>
</ul>
<hr>
<h3 id="객체와-인스턴스">객체와 인스턴스</h3>
<ul>
<li><p>클래스를 메모리에 올려 인스턴스를 만드는 과정을 &#39;클래스의 인스턴스화&#39;라고 한다.</p>
</li>
<li><p>이렇게 만들어진 객체가 &#39;인스턴스&#39;이다.</p>
</li>
</ul>
<br/>

<h4 id="객체의-구성요소">객체의 구성요소</h4>
<ul>
<li><p>객체는 &#39;속성&#39;과 &#39;기능&#39;으로 이루어져 있으며, 이를 객체의 &#39;멤버&#39;라고 한다. </p>
</li>
<li><p>프로그램의 측면에선 데이터(멤버 변수)와 메서드의 집합체이다.</p>
</li>
</ul>
<pre><code class="language-java">class TV {
    // 속성(멤버변수)
    boolean power; // 엔진 상태
    int channel;  // 속도

    // 기능(메서드)
    void power(){ power = !power; } //  전원 on/off 기능
    void channelUp { channel++; } // 채널 올리기
    void channelDown { channel--; } // 채널 내리기
}

/*
해당 예시에서 멤버변수의 기본값을 세팅해주지 않은 건, 
인스턴스 생성 시 자동으로 자료형의 기본값으로 세팅되기 때문이다.
*/</code></pre>
<br/>

<hr>
<h3 id="인스턴스의-생성과-사용">인스턴스의 생성과 사용</h3>
<blockquote>
</blockquote>
<p>인스턴스 생성 : <code>new</code> 연산자로 인스턴스를 메모리 위에 로딩.
인스턴스 사용 : 해당 클래스 타입의 참조변수에 인스턴스의 주소를 저장해서 사용.</p>
<pre><code class="language-java">class Tv {
    boolean power;
    int channel;

    // 기능(메서드)
    void power(){ power = !power; } 
    void channelUp { channel++; } 
    void channelDown { channel--; } 
}

class TvInstance {
    public static void main(String args[]){
         Tv t; // 참조변수 생성 
        t = new Tv(); // 인스턴스 생성
        t.channel = 7; // 채널의 값 7
        t.channelUp(); // 채널 올리기 메서드 호출
        System.out.println(t.channel); // 8
    }
}</code></pre>
<p><img src="https://velog.velcdn.com/images/ho_c/post/89719abe-1029-4e8d-ab21-4f84d4383180/image.JPG" alt=""></p>
<p><img src="https://velog.velcdn.com/images/ho_c/post/8e8b6dcd-390f-47d0-88b9-adec4284dbcb/image.JPG" alt=""></p>
<p><img src="https://velog.velcdn.com/images/ho_c/post/6619dec8-2e91-478e-b125-de17c136eb9b/image.JPG" alt=""></p>
<ul>
<li>인스턴스는 참조변수를 통해서만 다룰 수 있다. 
즉, 참조변수는 JVM의 heap 영역에 로딩된 인스턴스의 주소를 저장한다. 따라서 <strong>참조변수와 인스턴스의 타입은 일치해야 한다.</strong></li>
</ul>
<br/>

<hr>
<h3 id="객체-배열">객체 배열</h3>
<blockquote>
<p>인스턴스의 참조변수 역시 배열로 다룰 수 있다. 
즉, 인스턴스의 주소가 저장된 배열로 다수의 객체에 쉽게 접근이 가능하다.</p>
</blockquote>
<pre><code class="language-java">Tv tvl, tv2, tv3;  == Tv[] tvArr = new Tv[3];</code></pre>
<pre><code class="language-java">class Tv {
    boolean power;
    int channel;

    // 기능(메서드)
    void power(){ power = !power; } 
    void channelUp { channel++; } 
    void channelDown { channel--; } 
}

class TvInstanceArr {
    public static void main(String args[]){
         Tv[] tvArr = new Tv[3]; // 객체 배열 생성, tvArr는 객체배열의 참조변수

        // 객체배열에 인스턴스 저장
        for(int i=0; i&lt;tvArr.length; i++){
            tvArr[i] = new Tv();
        }

        // 배열로 인스턴스에 접근
        tvArr[0].channelUp();
        tvArr[1].channel = 12;
        tvArr[2].power();
    }
}</code></pre>
<blockquote>
<p>위 코드를 구조화 시키면 다음과 같다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/ho_c/post/a0842adf-c841-47c1-ba8e-9f423bca673d/image.jpg" alt=""></p>
<ul>
<li>객체 배열은 웹개발 시, DTO를 다룰 때 중요하게 쓰인다.</li>
</ul>
<hr>
<h3 id="클래스의-또-다른-정의">클래스의 또 다른 정의</h3>
<blockquote>
<p>프로그래밍의 관점에선 클래스는 데이터를 저장하는 형태이다.</p>
</blockquote>
<p><strong>① 데이터와 함수의 결합</strong></p>
<ul>
<li>데이터를 저장하는 공간은 다음과 같이 발전되었다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/ho_c/post/9765a662-32ec-4687-a8a0-3663ca891609/image.jpg" alt=""></p>
<ul>
<li><p>생각해보면 당연한 이야기이다. 하나의 데이터를 위해 <strong>&#39;변수&#39;</strong>가 생겼고, 이를 더 많이 다루기 위해 <strong>&#39;배열&#39;</strong>이 생겼다. 하지만 배열은 같은 타입만 가능하다. 
따라서 자료형에 상관없이 저장하는 <strong>&#39;구조체&#39;</strong>를 만들었고, 이를 다시 관련 기능과 함께 다루기 위해 <strong>&#39;클래스&#39;</strong>가 만들어졌다.</p>
</li>
<li><p>즉, 프로그래밍 관점에서 클래스는 공통된 목적으로 관계된 서로 다른 <strong>&#39;멤버 변수&#39;</strong>와 이와 관련된 작업을 하는 <strong>&#39;메서드&#39;</strong>의 집합이다.</p>
</li>
</ul>
<br/>

<p><strong>② 사용자정의 타입</strong></p>
<ul>
<li><strong>&#39;기본 자료형&#39;</strong>은, 보편적으로 사용될 자료형을 언어 개발자들이 미리 만들어 제공한 것이다.
이를 반대로 보면 언어 개발자들은 기본형 이외의 것은 예측할 수가 없다. </li>
<li><em>그건 사용하는 프로그래마다 다르니까*</em></li>
</ul>
<ul>
<li>때문에 프로그래머는 멤버 변수, 메서드, 제어자를 사용해서 자신에게 필요한 <strong>&#39;참조 자료형&#39;</strong>을 클래스로 구현한다.</li>
<li>즉, 프로그래머가 &#39;관계성&#39;을 파악해서 하나의 자료형(부품)을 만들어내는 것이 &#39;객체지향&#39;의 핵심이다.</li>
</ul>
<br/>

<h3 id="『-핵심-정리-』">『 핵심 정리 』</h3>
<ul>
<li><p>클래스는 객체의 설계도이며, 이를 메모리에 로딩한게 &#39;인스턴스&#39;다.</p>
</li>
<li><p>클래스에 소속된 변수와 메서드를 &#39;멤버&#39;라고 한다.</p>
</li>
<li><p>프로그래밍 관점에선 클래스는 발전된 데이터 저장방식 <strong>(데이터-함수의 집합)</strong></p>
</li>
<li><p>인스턴스는 같은 타입의 참조변수로 접근해서 사용할 수 있다. 참조변수는 배열로 다룰 수 있다.</p>
</li>
<li><p>참조변수와의 연결이 끊어지면 Garbage Collector가 메모리 상에서 자동으로 없앤다.</p>
</li>
</ul>
<hr>
<h2 id="3-변수와-메서드">3. 변수와 메서드</h2>
<h3 id="위치에-따른-변수의-종류">위치에 따른 변수의 종류</h3>
<blockquote>
<p>변수는 선언 위치에 따라 크게 세 종류가 있으며, 이에 따라 <strong>&#39;생성 시기&#39;</strong>가 전부 다르다.
즉, 어디에 변수를 선언하는지에 따라서 변수를 사용할 수 있는 지점들이 다르다.</p>
</blockquote>
<h4 id="변수의-분류">변수의 분류</h4>
<p><img src="https://velog.velcdn.com/images/ho_c/post/1241d782-f719-419a-9d47-99ea03944e73/image.JPG" alt=""></p>
<ul>
<li><p>선언 위치는 <strong>&#39;클래스 영역 / 이외 영역(메서드, 생성자, 초기화블럭)&#39;</strong>으로 나뉜다.
이 중 클래스 영역의 멤버 변수를 제외하면 모두 &#39;지역 변수&#39;이다.</p>
</li>
<li><p>지역 변수 : <strong>메서드 영역(scope)</strong> 안에서 사용되고, 사용 후엔 공간이 반환된다.</p>
</li>
<li><p>멤버 변수 : <code>static</code>이 붙으면 <strong>&#39;클래스 변수&#39;</strong>, 아니면 <strong>&#39;인스턴스 변수&#39;</strong></p>
</li>
</ul>
<br/>


<h4 id="변수의-생성시점">변수의 생성시점</h4>
<p><img src="https://velog.velcdn.com/images/ho_c/post/1b38b2f4-39da-49e9-8612-7b3b8c3e7ef6/image.JPG" alt=""></p>
<br/>

<h4 id="지역-변수">지역 변수</h4>
<ul>
<li><p>메서드 내(선언부, 구현부)에 선언되어 메서드 시작부터 끝까지만 사용이 가능하다.
즉, 메서드가 실행될 때 저장공간이 만들어지고 메서드 호출이 끝나면 저장공간이 반환된다.</p>
</li>
<li><p>영역 자체는 블럭<code>{}</code>을 중심으로 구분된다.
물론 메서드의 매개변수도 지역변수이지만, 애초에 모든 영역은 블럭으로 구분된다. 
따라서 조건, 반복문이든 메서드든 블럭으로 구분된 영역 안에서만 지역변수가 사용된다.</p>
</li>
</ul>
<blockquote>
<p>main() 안에서 일반적으로 사용했던 변수들도 모두 지역변수이다.</p>
</blockquote>
<br/>

<h4 id="인스턴스-변수">인스턴스 변수</h4>
<ul>
<li><p>클래스가 <code>new</code> 연산을 통해서 heap 영역에 생성될 때, 인스턴스 내부에 함께 생성된 저장공간이다.
때문에 인스턴스가 생성되지 않으면 사용할 수 없다. 동시에 인스턴스가 사라지면 함께 사라진다.</p>
</li>
<li><p>인스턴스는 &#39;독립적&#39;이다. 곧 만들어지는 인스턴스마다 메모리 공간이 다르다는 것.
때문에 인스턴스 변수 역시, 인스턴스 별로 변수명이 동일해도 다르게 유지할 수 있다.</p>
</li>
</ul>
<br/>

<h4 id="클래스-변수">클래스 변수</h4>
<ul>
<li><p>인스턴스와 같은 클래스 영역에 작성되지만, 변수 앞에 <code>static</code>이 붙어 있다.
클래스 변수는 <strong>&#39;클래스 데이터&#39;</strong>가 &#39;선언, 생성&#39;될 때, JVM의 method area에 단 &#39;하나&#39;만 생성된 저장 공간이다.</p>
<blockquote>
<p>참조변수의 선언이나 객체의 생성처럼 클래스 정보가 필요할 때, 클래스가 메모리에 로딩된다.</p>
</blockquote>
</li>
<li><p>즉, 모든 인스턴스가 하나의 클래스변수를 공유해서 사용한다.<br>그래서 모든 인스턴스가 공통적으로 유지해야하는 경우에 클래스 변수로 선언한다.</p>
</li>
<li><p>클래스가 메모리에 로딩 될 때, 함께 생성되어 프로그램 종료 시까지 유지된다. </p>
</li>
<li><p><code>클래스명.클래스변수명</code>의 형식으로 참조하여 사용하며, 인스턴스를 생성하지 언제든지 호출할 수 있다.</p>
</li>
<li><p>접근제한자 <code>public</code>을 사용하면 &#39;전역 변수&#39;의 성격을 갖는다.</p>
</li>
</ul>
<br/>

<p><strong>클래스변수와 인스턴스 변수 예시</strong></p>
<blockquote>
<p>카드를 예시로 두 차이를 간단하게 살펴보자</p>
</blockquote>
<pre><code class="language-java">class card{
    String kind;
    int number;

    static int width = 100; // 폭
    static int height = 200; // 높이
}</code></pre>
<ul>
<li><p>카드 클래스를 메모리에 로딩하면 카드 인스턴스가 만들어진다. 곧 서로 다른 종류의 카드들이 만들어진다.</p>
</li>
<li><p>종류와 숫자는 인스턴스별로 달라야 하니까, 인스턴스 변수이다.
반대로 카드의 규격은 모두 같아야 하니까, 클래스 변수로 선언했다.</p>
</li>
<li><p>만약 카드 규격을 바꾸고 싶으면, 클래스 변수만 수정해서 모든 카드 인스턴스의 규격을 바꾼다.</p>
</li>
</ul>
<br/>

<h3 id="『-핵심-정리-』-1">『 핵심 정리 』</h3>
<ul>
<li><p>변수는 선언 위치에 따라 멤버, 지역으로 구분된다.</p>
</li>
<li><p>다시 멤버는 <code>static</code>의 유무에 따라 클래스 변수와 인스턴스 변수로 구분된다.
클래스 변수에 <code>public</code>을 붙이면 &#39;전역 변수&#39;의 성질을 가진다.</p>
</li>
<li><p>지역 변수는 블럭 내에 선언된다. 
주로 메서드 내에 선언되며, 메서드 실행시 만들어지고 메서드가 종료되면 반환된다.</p>
</li>
<li><p>공통된 값을 유지할 때는 <strong>&#39;클래스 변수&#39;</strong> / 인스턴스마다 다른 값일 때는 <strong>&#39;인스턴스 변수&#39;</strong></p>
</li>
</ul>
<hr>
<h3 id="메서드">메서드</h3>
<blockquote>
<p>메서드는 특정 작업을 수행하는 문장들을 하나로 묶은 것이다.</p>
</blockquote>
<ul>
<li><p>개인적으로 프로그램은 컴퓨터가 사용자의 목적에 맞게 어떤 기능을 수행하는 것이 본질이라 생각한다. 그리고 이를 구현하는 방식이 &#39;연산&#39;인 것인 것이고.
이런 맥락에서 메서드는 프로그램의 최소 단위라고 생각한다. 즉, 국소적인 기능을 담당하는 게 &#39;메서드&#39;라는 것이다.</p>
</li>
<li><p>메서드 자체는 사용자가 입력만 넣으면, 원하는 결과를 도출해낸다. 그래서 사용자는 메서드의 세부적인 원리를 알지 않고, 쉽게 사용할 수 있다.</p>
<blockquote>
<p>리모컨의 원리를 알고 쓰는 사람이 몇이나 되겠음.</p>
</blockquote>
</li>
</ul>
<br/>

<h4 id="메서드를-사용하는-이유">메서드를 사용하는 이유</h4>
<p><strong>① 높은 재사용성</strong></p>
<ul>
<li>메서드를 한번 만들어 놓으면 언제든지 호출해서 사용할 수 있다. </li>
</ul>
<p><strong>② 중복된 코드의 제거</strong></p>
<ul>
<li>반복되는 코드는 메서드 하나로 묶어 관리할 수 있다. 
이 자체로도 코드의 재사용성을 높이지만 제일 중요한 건 &#39;유지보수&#39;가 편리해진다.</li>
</ul>
<p><strong>③ 프로그램의 구조화</strong></p>
<ul>
<li><p>프로그램은 <code>main()</code> 안에서 실행된다. 가볍게 100줄 정도는 읽을 수 있지만 실제 애플리케이션의 main은 기본이 그 이상이다.
그만큼 처리되는 작업들이 많다는 것인데, 이걸 절차적으로 쭉 나열한다 생각해보면 답이 없다.</p>
</li>
<li><p>그래서 메서드 단위로 필요한 작업들을 구분하여 main을 최대한 구조적으로 만들어준다. 
(걍 코드 가독성을 높여준다는 뜻으로만 이해해도 됨)</p>
</li>
<li><p>또 클래스의 기능들을 정의할 때도 미리 메서드로 기능들을 구조화 해두면 구현부만 작성해주면 된다.
```java
public static void main(String args[]){
  int[] numArr = new int[10];</p>
<p>  initArr (numArr); // 1. 배열을 초기화
  printArr (numArr); // 2. 배열을 출력
  sortArr (numArr); // 3. 배열을 정렬
  printArr (numArr); // 4. 배열을 출력
}
/*
실제로 쓰면 인스턴스들을 메모리에 미리 로딩 시켜놓고 그때마다 메서드들을 호출해서 사용한다.
또는 메서드 단위로 인스턴스(계층)들을 오고 가며 작업을 처리한다.</p>
</li>
<li><p>/</p>
<pre><code></code></pre></li>
</ul>
<hr>
<h3 id="메서드의-선언과-구현">메서드의 선언과 구현</h3>
<blockquote>
<p>메서드는 &#39;선언부 / 구현부&#39;로 이루어져 있다.</p>
</blockquote>
<pre><code class="language-java">// 선언부
(제어자) 반환타입 메서드명 (매개 변수)

// 구현부
{
    // 호출 시 수행될 코드 
}

// 예시
int add (int x, int y){
    return x+y;
}</code></pre>
<hr>
<h4 id="선언부">선언부</h4>
<blockquote>
<p>메서드명, 매개변수 선언, 반환타입으로 구성</p>
</blockquote>
<ul>
<li><p>선언부는 메서드의 기능, 필요한 데이터, 반환되는 결과값에 대한 정보를 제공해준다. 
또 맨 앞부분엔 제어자를 붙여서 사용범위나 접근성을 제어할 수 있다.</p>
</li>
<li><p>메서드 선언부가 변경되면 호출하는 부분들도 모두 변경해야되는 걸 주의하자.</p>
</li>
</ul>
<br/>

<p><strong>1) 매개변수 선언</strong></p>
<ul>
<li>매개변수는 사용자로부터 값을 전달받는 메모리 공간. 전달 방식은 후술하겠지만 일단은 메서드가 작업하는데 필요한 값을 받을 변수를 선언한다고 이해하자.</li>
<li>일반 변수선언과 달리 같은 타입이라도 변수 타입을 생략할 수 없다.</li>
<li>매개변수의 수는 제한이 없다. (배열, 참조변수, 가변인자 등)</li>
<li>메서드가 종료되면 필요없기 때문에, <strong>&#39;지역 변수&#39;</strong>이다.</li>
</ul>
<p><strong>2) 메서드명</strong></p>
<ul>
<li>메서드명은 해당 메서드가 어떤 기능을 하는지 이름만 보고 알 수 있게 명명하는 것이 좋다.</li>
</ul>
<p><strong>3) 반환타입</strong></p>
<ul>
<li>매개변수의 반대, 메서드의 작업 결과에 대한 정보이다. 반환값이 어떤 형태인지 적어줘야 하는데, 이게 없으면 오류난다.</li>
<li>반환타입이 없을 때는 <code>void</code>라고 적는다.</li>
</ul>
<hr>
<h4 id="구현부">구현부</h4>
<blockquote>
<p>실제 메서드의 기능을 구현한다.</p>
</blockquote>
<p>*<em>1) return문 *</em></p>
<ul>
<li><p>반환타입이 <code>void</code>가 아니면 구현부 내에는 return문이 꼭 있어야 한다.
사실, void도 return문이 존재한다. 컴파일러가 자동으로 추가해주는 것뿐이다.</p>
</li>
<li><p>return문은 단 하나의 값만 반환할 수 있다.</p>
</li>
<li><p>이 값의 타입은 선언부의 반환타입과 일치하거나, 적어도 산술변환, upcasting이 가능한 것이다여 한다.
예를 들어 주소도 될 수 있고, 객체배열도 될 수 있고 상속 관계 안에서 자손 타입도 가능하다.</p>
</li>
<li><p>굳이 값 자체 아니더라도 연산결과의 타입만 맞다면 수식으로 해도 괜찮다. </p>
<p>  int add(int a, int b){</p>
<pre><code>  return a+b; // 수식을 계산한 결과를 반환</code></pre><p>  }</p>
</li>
<li><p><code>if</code>문 안에 <code>return</code>을 넣는 경우, if문이 실행되지 않을 수 있기 때문에 컴파일 에러가 생긴다.
그래서 <code>if-else</code>로 항상 반환값이 있게 만들어야 한다.</p>
<p>  int max(int a, int b){</p>
<pre><code>  if(a&gt;b){
      return a; // false일 경우에는 반환값이 없다.
  } else {
      return b; // 이제 어떤 경우든 반환값이 있다.
  }</code></pre><p>  }</p>
</li>
</ul>
<br/>


<p><strong>2) 지역변수</strong></p>
<ul>
<li><p>앞서 말한 것처럼 메서드 내에 선언된 변수는 그 메서드 안에서만 사용이 가능하다. </p>
</li>
<li><p>이를 지역변수라 하고, 매개변수도 포함된다. </p>
</li>
<li><p>메서드가 호출되면 저장공간이 생성되고, 메서드가 작업 결과를 반환하면 사라진다.</p>
</li>
</ul>
<br/>

<hr>
<h3 id="메서드의-호출">메서드의 호출</h3>
<blockquote>
<p>정의된 메서드를 사용하려면 호출해야만 한다.
호출 시, <code>()</code>에 메서드에 지정된 값들을 전달해줘야 한다.</p>
</blockquote>
<h4 id="인자argument-매개변수parameter">인자(argument), 매개변수(parameter)</h4>
<ul>
<li><p>메서드 정의 시, 사용자가 넘겨줄 값을 &#39;매개변수&#39;라고 한다.
반대로 호출 시에, 사용자가 넘겨주는 값이 &#39;인자&#39;이다.</p>
</li>
<li><p>인자는 매개변수의 타입, 순서, 개수 모두 일치해야 한다.
적어도 &#39;자동형변환&#39;이 일어나야 한다. ( 다형성으로 응용 )</p>
</li>
</ul>
<br/>

<h4 id="메서드의-실행흐름">메서드의 실행흐름</h4>
<ul>
<li><p>같은 클래스 내의 메서드끼리는 &#39;참조변수&#39;없이 서로 호출이 가능하다.
다른 클래스의 메서드를 사용하려면 인스턴스를 생성해, 참조변수로 메서드 호출을 해야 한다.</p>
</li>
<li><p>단, static메서드는 같은 클래스 내의 인스턴스 메서드를 호출 할 수 없다.
왜냐면 static메서드가 메모리에 로딩된 시점에 인스턴스가 생성되지 않았을 수도 있기 때문이다.</p>
</li>
</ul>
<pre><code class="language-java">class MyMath {
    long add(long a, long b) {
        long result = a+b;
        return result;
    }

    long subtract (long a, long b) { return a - b; }
    long multiply(long a, long b) { return a * b; }
    double divide (double a, double b) { return a / b; }

    // 같은 클래스 내의 메서드 간 호출
    void callInnerMethod(long x, long y){ // instance Method
        add(x, y); // instance Method
    }
}</code></pre>
<br/>

<hr>
<h3 id="jvm의-메모리-구조">JVM의 메모리 구조</h3>
<blockquote>
<p>JVM은 자바 프로그램 실행을 위한 가상머신이다.
머신이 프로그램을 실행하기 위해선 메모리 영역이 필요하며, 이를 운영체제로 부터 할당받는다.
그럼 JVM은 용도에 따라서 여러 영역으로 나누어 관리한다. </p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/ho_c/post/5ac701a1-4441-44fd-a08d-85df7fc63db3/image.jpg" alt=""></p>
<blockquote>
<p>JVM의 메모리 영역은 5가지이다.</p>
</blockquote>
<ul>
<li><strong>Method Area : 클래스의 정보, 클래스 변수, 메소드 코드 등을 저장하는 공간</strong><ul>
<li>JVM은 프로그램 실행 중 어떤 클래스가 사용(선언, 생성)되면, 
해당 클래스의 클래스 파일을 분석해서 클래스 데이터를 메서드 영역에 저장한다.</li>
<li>JVM이 시작될 때 생성, 모든 스레드가 공유한다. </li>
<li>따라서 클래스 변수의 생성 주기는 <code>프로그램 실행 → 종료</code> 까지다.</li>
</ul>
</li>
</ul>
<ul>
<li><strong>Heap : 인스턴스 저장 공간</strong><ul>
<li>동적으로 생성되는 인스턴스는 이곳에 생성되며, 가비지 컬렉션의 대상이 된다.</li>
<li>모든 스레드가 공요한 공간이다.</li>
<li>참조변수와 인스턴스의 연결이 끊기면, GC가 메모리에서 제거한다.</li>
</ul>
</li>
</ul>
<ul>
<li><strong>Call-Stack : 메서드 작업을 위한 메모리 공간</strong><ul>
<li>호출 메서드를 위한 공간들이 할당되며, 지역변수와 리턴값, 중간 연산 결과 등을 저장한다.</li>
<li>스레드마다 별도 생성되며, 메서드 호출마다 프레임이라는 공간을 생성해서 사용한다.</li>
<li>메서드가 종료되면 사용한 프레임도 반환되어 비워진다.</li>
<li>따라서 지역변수의 생성 주기는 <code>메서드 호출 → 메서드 종료</code> 까지다.</li>
</ul>
</li>
</ul>
<ul>
<li><p><strong>PC : 현재 수행중인 명령어의 주소를 저장하는 공간</strong></p>
</li>
<li><p><strong>Native Method Stack : 자바 이외의 언어로 작성된 네이티브 코드를 위한 스택 영역</strong>  </p>
</li>
</ul>
<hr>
<blockquote>
<p><strong>번외. Call-Stack에 스레드가 나온 김에 스레드에 대해서 알아보자.</strong></p>
</blockquote>
<h4 id="스레드thread">스레드(Thread)</h4>
<p>: 프로세스 내에서 실행되는 CPU의 작업 단위</p>
<blockquote>
<p><strong>스레드를 이해하려면, 프로세스에서부터 시작하는게 좋다.</strong></p>
</blockquote>
<h4 id="스레드가-나오게-된-배경">스레드가 나오게 된 배경</h4>
<h4 id="전제">전제</h4>
<ul>
<li>스레드는 ‘프로세스‘의 한계를 극복하기 위해 만들어 졌다.</li>
</ul>
<h4 id="문제">문제</h4>
<ul>
<li>초기 컴퓨터는 한번에 하나의 프로세스만 처리할 수 있었다.</li>
</ul>
<h4 id="과정">과정</h4>
<ol>
<li><p><strong>프로세스</strong></p>
<ul>
<li><p>프로그램은 컴퓨터가 실행할 명령어의 집합이며, 프로그램이 실행되면 <strong>&#39;프로세스&#39;</strong>가 된다.</p>
</li>
<li><p>프로세스의 핵심은 CPU가 처리할 <strong>&#39;메모리 위에 독립된 공간&#39;</strong>를 가지는 것이다.</p>
</li>
<li><p>이 독립된 공간은 OS에 의해 배당된 가상주소공간이다.</p>
</li>
<li><p>즉, <strong>프로세스는 CPU가 처리할 작업이자 단위다.</strong></p>
</li>
<li><p>다만 한번에 한 프로세스만 처리가 가능하여 &#39;I/O&#39;같은 외부작업이 있다면 
CPU와 메모리는 대기상태가 된다.</p>
</li>
<li><p>즉, 자원낭비가 있었다. 이를 효율적이게 사용하고자 여러 프로세스를 부분적으로 실행했다.</p>
</li>
<li><p>이 때, 다른 프로세스의 시간이 오래걸리면 다음 프로세스은 대기 상태가 되는 문제가 있었다.</p>
</li>
</ul>
</li>
</ol>
<br/>

<ol start="2">
<li><p><strong>멀티태스킹</strong></p>
<ul>
<li><p>앞선 문제를 해결하고자 각 프로세스를 일정 시간동안 실행하는 <strong>&#39;멀티 태스킹&#39;</strong>이 생겼다.</p>
</li>
<li><p>멀티태스킹은 각 프로세스를 짧은 시간동안 실행해, 사용자의 입장에선 각 프로세스가 연속적, 동시적으로 사용되는 것처럼하는 방법이다.</p>
</li>
<li><p>하지만 프로세스는 독립적인 메모리 공간을 사용하기에 한계가 있었다.</p>
</li>
<li><p><strong>그 한계는 다음과 같다.</strong>
(1) 하나의 프로세스는 여러 작업을 동시에 할 수 없다.
(2) 프로세스 간 자원공유가 어려움
(3) 프로세스 간의 작업 전환, 컨텍스트 스위칭은 무거운 작업이다.
(4) 멀티코어 CPU를 효율적으로 사용할 수 없다.</p>
</li>
</ul>
</li>
</ol>
<blockquote>
<p>따라서 프로세스를 더 잘게 쪼개기로 하는데, 그게 ‘스레드’이다.</p>
</blockquote>
<br/>

<h4 id="정리">정리</h4>
<ul>
<li><p>스레드는 프로세스 내의 작업 단위로 하나의 프로세스는 한 개 이상의 스레드를 가진다.</p>
</li>
<li><p>각 스레드는 프로세스의 메모리 안에서 독립된 stack영역을 가진다.</p>
</li>
<li><p>이 영역은 포인터로 구별되기 때문에 포인터만 전환하면 작업 전환이 쉽다.</p>
</li>
<li><p>또한, 프로세스 내의 heap영역 안의 자원을 공유한다. 따라서 한 프로세스 내의 데이터 공유가 쉽다.</p>
</li>
<li><p>여러 개의 스레드를 동시에 실행하는 것을 <strong>&#39;멀티스레딩&#39;</strong>이라고 한다.</p>
</li>
<li><p>멀티스레딩을 사용하면 멀티코어 CPU에서 동시적, 병렬적인 실행을 구현할 수 있다.</p>
</li>
<li><p>하지만 프로세스의 자원공유는 스레드의 동시성 문제가 발생할 수 있으므로 주의해야 한다.</p>
</li>
</ul>
<blockquote>
<p>결과적으로 스레드라는 더 작은 단위로 인해, 작업을 처리하는 <strong>‘멀티태스킹, 멀티스레딩, 멀티프로세싱’</strong>의 효율을 높일 수 있다</p>
</blockquote>
<hr>
<h4 id="jvm과-메서드의-관계">JVM과 메서드의 관계</h4>
<pre><code class="language-java">class CallStackTest {
    public static void main(String[] args) {
        firstMethod(); // static 메서드는 객체 생성없이 호출가능하다.
      }

    static void firstMethod() {
        secondMethod();
    }

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

/* ▼ 실행결과
secondMethod()
*/</code></pre>
<p><img src="https://velog.velcdn.com/images/ho_c/post/32586eb4-7582-4d50-bfd9-1c92eaca996a/image.jpg" alt=""></p>
<ul>
<li><p>메서드가 호출되면 수행에 필요한 메모리 공간을 콜스택에 할당받는다.
이 공간은 메서드 별로 구별되어 있다.</p>
</li>
<li><p>첫번째 호출된 메서드의 공간은 콜스택의 맨 밑에 마련되고,  순서대로 바로 위에 다음 호출된 메서드들의 공간이 생긴다.</p>
</li>
<li><p>다른 메서드를 호출한 메서드(caller)는 대기 상태가 된다.</p>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/ho_c/post/b5114685-c67d-4d46-a683-a13dd2f78f9b/image.jpg" alt=""></p>
<ul>
<li><p>역으로 메서드가 종료되면, 맨 위에서부터 차례대로 콜스택에 배당된 메모리 공간이 반환된다.
이때 사용한 지역변수들도 함께 사라진다.</p>
</li>
<li><p>반환타입이 있는 메서드는 종료하면서 결과값을 자신을 호출한 메서드에게 반환한다.
그러면 대기 상태에 있던 메서드를 이를 갖고 작업을 한다.</p>
</li>
</ul>
<hr>
<h3 id="매개변수">매개변수</h3>
<blockquote>
<p>매개변수는 메서드 내에서 사용한 데이터를 넘겨받는 변수(공간)이다.</p>
</blockquote>
<p><strong>메서드에 값을 전달하는 방식</strong></p>
<blockquote>
<p>매개변수를 알아보기 앞서, 데이터를 전달하는 두 가지 방식을 살펴보자.</p>
</blockquote>
<h4 id="1-call-by-value">1. Call by Value</h4>
<ul>
<li>가장 기본이 되는 전달 방식</li>
<li>값을 매개변수에 복사하는 방식</li>
<li>값이 복사되었기 때문에 원본의 값을 변경되지 않는다.</li>
<li>매개변수의 타입은 &#39;기본형&#39;이다.</li>
</ul>
<pre><code class="language-java">class CallByValue{
    public static void main(String[] args){
        int a = 10;
        cbValue(a);
        System.out.println(&quot;main() : &quot; + a);
    }

    void cbValue(int b){
        System.out.println(&quot;input : &quot;+ b);
        b = 20;
        System.out.println(&quot;change : &quot;+ b);
    }
}
/* ▼ 실행결과
input : 10
change : 20
main() : 10
*/</code></pre>
<br/>

<h4 id="2-call-by-adress">2. Call by Adress</h4>
<ul>
<li><strong>참조변수는 주소를 저장한다.</strong>
따라서 주소를 매개변수에 복사해서 전달하는 방식</li>
<li>본질은 Call by Value와 다르지 않다. 
다만 주소를 통해 원본 데이터에 접근할 수 있다.</li>
<li>매개변수의 타입은 &#39;참조형&#39;이다.</li>
</ul>
<pre><code class="language-java">class Adress{ int a; }

class CallByAdress{
    public static void main(String[] args){
        Adress adr =  new Adress();
        adr.a = 10;
        System.out.println(&quot;변경 전 adr.a값 : &quot; + adr.a);
        cbAdress(adr);
        System.out.println(&quot;변경 후 adr.a값 : &quot; + adr.a);
    }

    void cbAdress(Adress b){
        System.out.println(&quot;input : &quot;+ b.a);
        b.a = 20;
        System.out.println(&quot;change : &quot;+ b);
    }
}
/* ▼ 실행결과
변경 전 adr.a값 : 10
input : 10
change : 20
변경 후 adr.a값 : 20 
*/</code></pre>
<br/>

<h4 id="번외-call-by-reference">번외. Call by Reference</h4>
<ul>
<li>Java가 아닌 C++에서 사용되는 방식</li>
<li>C++에선 개발자가 직접 주소를 조작할 수 있다. </li>
<li>그래서 매개변수와 선언된 변수가 같은 메모리 영역을 바라보게 할 수 있다.</li>
</ul>
<br/>

<hr>
<h3 id="참조형-반환타입">참조형 반환타입</h3>
<blockquote>
<p>매개변수뿐만 아니라 반환타입도 &#39;참조형&#39;이 될 수 있다.
앞서 본 것처럼, <strong>타입이 참조형이라는 것은 메서드가 &#39;객체의 주소&#39;를 반환한다는 것이다.</strong></p>
</blockquote>
<pre><code class="language-java">class Data { int x; }

class ReferenceReturnEx {
    public static void main(String[] args) {
        Data d = new Data();
        d.x = 10;

        Data d2 = copy(d);
        System.out.println(&quot;d.x = &quot;+d.x);
        System.out.println(&quot;d2.x = &quot;+d2.x);
    }

    static Data copy (Data d) {
        Data tmp = new Data(); 
        tmp.x = d.x;

        return tmp;
    }
}

/* ▼ 실행결과
d.x = 10;
d2.x = 10;
*/</code></pre>
<br/>

<hr>
<h3 id="재귀-호출">재귀 호출</h3>
<blockquote>
<p>메서드 내부에서 자신을 다시 호출하는 것을 &#39;재귀 호출&#39;이라 한다.</p>
</blockquote>
<pre><code class="language-java">void method(){
    method();
}</code></pre>
<ul>
<li><p>메서드 호출 자체는 명령어 수행이다. 즉, 재귀 함수는 내부적으로 자신을 재호출하는 것이다.
그래서 일반 메서드 호출이랑 다를게 없다.</p>
</li>
<li><p>호출된 메서드는 &#39;call by value&#39;로 복사한 값으로 작업한다. 
그래서 caller와 상관없이 작업할 수 있다.</p>
</li>
<li><p>다만, 재귀함수는 &#39;제어문&#39;으로 특정 조건에서 호출이 멈추게 해야한다.</p>
</li>
<li><p>재귀함수는 외부 메서드처럼 작업 후, 끝난다고 생각하면 안된다.
기존 코드가 똑같이 실행되기에, 호출을 끝내는 조건을 설정하지 않으면 &#39;무한 호출&#39;을 하게 된다.</p>
</li>
<li><p>즉, 호출을 끝내는 조건이 있어야 &#39;호출 스택&#39; 맨 위 메서드부터 차례대로 처리될 수 있다.
(무한 호출은 결국 호출 스택의 저장 범위를 넘어서 &#39;스택 오버플로우&#39;에러는 발생시킴)</p>
</li>
</ul>
<br/>

<p><strong>재귀호출은 대부분 반복문으로도 표현이 가능하다.</strong>
그리고 반복문에선 매개변수 저장이나, 반환 과정이 생략되어 속도가 더 빠르다.</p>
<blockquote>
<p><strong>그럼 굳이 왜 쓸까?</strong></p>
</blockquote>
<ul>
<li><p>코드의 관점에서 볼 때, 반복문은 <strong>&#39;같은 작업을 하는 연속된 코드&#39;</strong>를 한번에 처리하는 문법이다.</p>
</li>
<li><p>같은 맥락에서 메서드도 프로그램 안에서 반복되는 코드를 하나로 묶어서, 메서드란 형식으로 호출해서 쓸 수 있게 한 것이다.</p>
</li>
<li><p>재귀호출도 메서드 안 <strong>&#39;공통, 중복된 코드&#39;</strong>를 또 호출하는 것이다. 다만 그 대상이 매개변수가 다른 자신인 걸 제외하면 말이다.</p>
</li>
<li><p>따라서, 재귀호출을 통해 코드를 간결하게 만들어 준다.</p>
</li>
</ul>
<pre><code class="language-java">class Factorial{
    public static void main(String args[]){

        int input = 4;

        // 재귀 호출
        int result1 = factorial(input);

        // 반복문
        int result2 = 1;
        for(int i=1; i&lt;=input; i++){
            result2 *= i;
        }
    }

    static int factorial(int n){
        // 매개변수의 유효성 검사
        if(n &lt;= 0 || n &gt; 12) return -1;

        if(n == 1) {
            return 1;
        } 
        return n * factorial(n-1);
    }
}</code></pre>
<br/>

<hr>
<h3 id="클래스-메서드와-인스턴스-메서드">클래스 메서드와 인스턴스 메서드</h3>
<blockquote>
<p>변수처럼 메서드도 메모리 영역에 따라 구분된다.</p>
</blockquote>
<h4 id="클래스-메서드">클래스 메서드</h4>
<ul>
<li><p><code>static</code> 키워드가 붙어 있는 메서드</p>
</li>
<li><p>인스턴스 생성 없이 메서드 사용이 가능하다. <code>클래스명.메서드명(매개변수)</code></p>
</li>
<li><p>JVM의 Method Area의 일부분인 Class Area에 클래서 정보가 로딩될 때, 함께 로딩된다.</p>
</li>
<li><p>일반적으로 인스턴스 변수, 메서드와 관련되지 않을 경우 클래스 메서드로 정의한다.</p>
</li>
</ul>
<br/>

<h4 id="인스턴스-메서드">인스턴스 메서드</h4>
<ul>
<li><p><code>static</code> 키워드가 없는 메서드</p>
</li>
<li><p>인스턴스가 생성되야만 사용할 수 있다.</p>
</li>
<li><p>인스턴스 생성과 함께 heap영역에 메서드가 로딩된다.</p>
</li>
<li><p>인스턴스 변수를 사용하는 메서드는 일반적으로 인스턴스 메서드가 된다.</p>
</li>
</ul>
<br/>

<h4 id="정리-1">정리</h4>
<ol>
<li><strong>모든 인스턴스가 하나의 값을 유지해야 하는 멤버변수에는 <code>static</code>을 붙인다.</strong><ul>
<li>클래스는 하나지만, 인스턴스는 여러개가 가능하다. 
이 때, 공통으로 값을 유지해야되는 멤버변수는 클래스영역으로 로딩해서 공유되게 한다.</li>
</ul>
</li>
</ol>
<ol start="2">
<li><strong>클래스 변수는 인스턴스를 생성 안해도 사용가능하다.</strong></li>
</ol>
<ol start="3">
<li><p><strong>클래스 메서드는 인스턴스 변수를 사용할 수 없다.</strong></p>
<ul>
<li><p>메서드가 로딩된 시점에 인스턴스가 생성되어 있지 않으면 사용할 수 없다.
이로 인해 발생되는 문제를 막기 위해, 클래스 메서드는 인스턴스 변수를 내부적으로 사용할 없다.</p>
</li>
<li><p>반대로 인스턴스 변수나 메서드는 static이 붙은 변수, 메서드 사용이 가능하다.
왜냐면 인스턴스가 생성되면 이미 클래스 정보가 JVM에 로딩되었기 때문이다.</p>
</li>
</ul>
</li>
</ol>
<ol start="4">
<li><strong>인스턴스 멤버를 사용하지 않는다면 <code>static</code>을 붙이는게 효율적이다.</strong></li>
</ol>
<hr>
<h3 id="멤버-간의-참조와-호출">멤버 간의 참조와 호출</h3>
<blockquote>
<p>같은 클래스 멤버 간에는 인스턴스 생성 없이 참조, 호출이 가능하다.
<strong>단, 클래스 멤버는 인스턴스 멤버를 사용하려면 인스턴스 생성을 해야한다.</strong></p>
</blockquote>
<h4 id="예시">예시</h4>
<pre><code class="language-java">class MemberCall{
    int iv = 10; // 인스턴스 변수
    static int cv = 20; // 클래스 변수

    // 멤버 변수간 사용
    int iv2 = cv; // 가능
    static int cv2 = new MemberCall().iv; // 가능 (인스턴스 생성 O)
    // static int cv2 = iv; 불가능 (인스턴스 생성 X)

    // 클래스 메서드
    static void staticMethod(){
        System.out.println(cv); 
        // System.out.println(iv); 불가능 (인스턴스 생성 X)

        MemberCall c = new MemberCall();
        System.out.println(cv); 
    }

    // 인스턴스 메서드
       void instanceMethod1(){
        System.out.println(iv); 
        System.out.println(cv); 
        staticMethod();
    }

    void instanceMethod2(){
        System.out.println(iv); // 가능
        System.out.println(cv); // 가능
        staticMethod(); // 가능
        instanceMethod1(); // 가능
    }
}</code></pre>
<hr>
<h3 id="『-핵심-정리-』-2">『 핵심 정리 』</h3>
<ul>
<li><p>메서드는 특정 작업을 수행하는 문장들을 하나로 묶은 것이다.
그래서 코드의 재사용성과 유지보수에 좋다.</p>
</li>
<li><p>매개변수는 &#39;지역변수&#39;이며, 기본형은 &#39;값을 복사&#39; / 참조형은 &#39;주소를 복사&#39;한다.</p>
</li>
<li><p>멤버 변수처럼 메서드도 클래스, 인스턴스로 구분된다.</p>
</li>
<li><p>같은 클래스 내에 있을 때는 인스턴스 생성없이 참조할 수 있다.
다만 클래스 메서드의 인스턴스 메서드, 변수 사용은 제한된다.</p>
</li>
<li><p>메서드엔 반환타입이 있어야 한다. 없을 경우 void로 반환타입을 정의한다.
내부적으론 void도 return문이 존재한다.</p>
</li>
<li><p>if문 안에 return을 넣을 경우, 예외의 가능성 때문에 컴파일 에러가 생긴다.</p>
</li>
<li><p>반환타입은 선언부와 일치하거나, 산술변환이 가능해야 한다.</p>
</li>
</ul>
<hr>
<blockquote>
<p>도움이 되셨다면 <strong>&#39;좋아요&#39;</strong> 부탁드립니다 :)</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[Ch05. 배열(array)]]></title>
            <link>https://velog.io/@ho_c/Ch05.-%EB%B0%B0%EC%97%B4array</link>
            <guid>https://velog.io/@ho_c/Ch05.-%EB%B0%B0%EC%97%B4array</guid>
            <pubDate>Sun, 02 Apr 2023 11:52:28 GMT</pubDate>
            <description><![CDATA[<h1 id="들어가는-말">들어가는 말</h1>
<ul>
<li><p>변수는 저장공간이고, 각 저장방식에 따라서 자료형이 존재했고, 변수 하나에는 정해진 크기 안에서 하나의 값만 저장할 수 있었다.</p>
<blockquote>
<p>*<em>만약 같은 타입의 저장공간이 10개가 필요하다면? *</em></p>
</blockquote>
</li>
<li><p>이를 위해 변수 10개를 선언하면 되겠지만, 사용할 변수가 100개만 넘어가도 하나, 하나 값을 정하고, 반대로 수정하면 100개 중의 하나를 찾아야 한다.</p>
</li>
<li><p>곧 변수가 늘어날수록 코드의 가독성도, 유지 보수성도 떨어진다. 
이 때문에 같은 타입의 변수를 여러 개를 묶어서 다른 하나의 변수에 연결해 쓰는 것이 바로 “배열”이다. 
추가로 변숫값의 입력도 반복 작업이라 반복문이나 조건문을 통해 이 배열을 관리할 수 있다.</p>
</li>
</ul>
<hr>
<h2 id="1-배열">1. 배열</h2>
<ul>
<li><p>같은 타입의 여러 변수를 하나의 묶음으로 다루는 것
앞서 말했듯 ‘같은 타입’의 변수를 여러 번 선언하는 건 손해다. 다루기도 어렵고 그래서 배열을 통해서 <strong>‘인덱스’</strong>와 <strong>‘길이’</strong>로 데이터를 다룰 수 있다.</p>
</li>
<li><p>배열 안의 데이터들은 저장공간이 연속적으로 배치된다. 곧 순서가 존재한다.</p>
</li>
<li><p>애초에 배열은 포인터(메모리 주소를 저장하는 변수)로 구현이 된다. 따라서 배열을 저장하는 변수 자체는 c에서 말하는 포인터 변수이다.</p>
</li>
<li><p>다만 자바는 사용자가 임의의 주소를 다룰 수 없게 했기 때문에(‘참조 변수’로만 주소를 다룰 수 있음.) 포인터 변수를 사용할 수는 없다.</p>
</li>
</ul>
<br/>

<h3 id="배열의-선언과-생성">배열의 선언과 생성</h3>
<h4 id="배열-선언">배열 선언</h4>
<ul>
<li>타입의 변수를 선언하고 <code>[]</code>를 붙인다.</li>
<li>[n]가 가리키는 값은 *(a+n)와 같은 의미이다.</li>
</ul>
<pre><code class="language-java">// 타입[] 변수명
int[] score;

// 타입 변수명[]
int score[];</code></pre>
<br/>


<h4 id="배열의-생성">배열의 생성</h4>
<ul>
<li>선언된 건 배열의 주소를 저장할 공간이다. 실제 배열은 메모리 상에 생성되어야 한다.
따라서 new 연산자와 함께 배열의 타입과 길이를 지정해야한다.</li>
</ul>
<pre><code class="language-java">int[] score;
score = new int[5]; // 길이가 5인 배열 생성</code></pre>
<p><img src="https://velog.velcdn.com/images/ho_c/post/9ad96bdc-dd6a-42d4-b00e-db6d1a1933b7/image.jpg" alt=""></p>
<ul>
<li>위 표에서 보듯, new에 의해 배열이 생성되면 int타입 데이터를 저장할 공간이 만들어진다.</li>
<li>저장공간은 int의 기본값 0으로 자동 초기화된다.</li>
<li>score는 배열의 주소를 저장하는 ‘참조변수’이며, 이 변수로 배열에 접근할 수 있다.</li>
</ul>
<br/>

<h3 id="배열의-길이와-인덱스">배열의 길이와 인덱스</h3>
<h4 id="인덱스index">인덱스(Index)</h4>
<ul>
<li>배열의 저장공간 자체는 <strong>‘요소’</strong>라고 하며, 그 요소에 붙은 주소를 <strong>‘인덱스’</strong>라고 한다.</li>
<li>인덱스는 <strong>‘0’</strong>부터 시작한다. 따라서 범위는 <strong>&#39;배열 길이-1&#39;</strong>이다.</li>
<li>따라서 배열을 다룰 때, 인덱스는 해당 요소 공간에 접근하는 <strong>‘포인터 변수의 역참조’</strong>이다.</li>
<li>즉, 선언된 변수 자체는 배열의 시작 주소를 저장한다. 
그리고 변수는 증감이 가능한데, 자료형에 따라 같은 +1이라 할지라도 주소의  관점에선 자료형의 크기에 따라 다음 인덱스가 가리키는 값의 시작 주소로 넘어간다.</li>
<li>그 이유는 주소의 중간값을 저장해도 별 쓸모가 없기 때문이다. 고로 a[0]은 *(a+0)과 같은 연산이다.</li>
</ul>
<pre><code class="language-java">int[] score = new int[50]; // 길이 : 50, 인덱스 : 0~49
score[48] = 49; // 49번째 요소에 49를 저장한다.</code></pre>
<p><img src="https://velog.velcdn.com/images/ho_c/post/88dbda1b-c553-4b55-97c6-8851e4784d0c/image.jpg" alt=""></p>
<ul>
<li>배열의 인덱스는 상수 외에도 변수, 수식으로도 구현이 가능하다. 
그래서 for문의 변수 I를 주로 인덱스로 사용한다.</li>
</ul>
<pre><code class="language-java">int[] score =  new int[10];

// 변수로 값 저장하기
for(int i=0; i&lt;10; i++){
    score[i] = i * 2;
}
// score[] = {0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20};


// 수식으로 값 꺼내오기
System.out.print(“출력 : ”);
for(int i=0; i&lt;5; i++){
    System.out.printf(“%d ”, score[i*2]);
}
// 출력 : 0 4 12 16</code></pre>
<ul>
<li>단, 수식이나 변수 사용 시엔 인덱스의 범위가 벗어난 값이 사용되면 안 된다. 
넘어가면 실행 시 에러가 발생한다. ArrayIndexOutOfBoundsException (컴파일러에서 걸러지지 않음)</li>
<li>그래서 반복문의 시작도 0으로 시작해서, 배열의 길이보다 작게 끝낸다.</li>
</ul>
<br/>

<h4 id="길이length">길이(Length)</h4>
<ul>
<li>배열 생성시, <strong><code>[]</code></strong>안에 들어가는게 길이, 곧 <strong>&#39;요소의 개수&#39;</strong>이다.</li>
<li>길이는 0과 양의 정수만 가능하며, <strong>int의 최대값 약 20억(10자리)</strong>까지 가능하다.</li>
<li>물론 길이가 <strong>0</strong>인 배열도 만들 수 있다.</li>
</ul>
<pre><code class="language-java">int[] arr1 = new int[100000]; // 길이가 100000인 배열
int[] arr2 = new int[0]; // 길이가 0인 배열</code></pre>
<br/>

<h4 id="배열명length">배열명.length</h4>
<ul>
<li>JVM은 모든 배열의 길이를 별도 관리한다.</li>
<li>따라서 <code>.length</code>를 통해 배열의 길이를 쉽게 구할 수 있다.</li>
<li>단, JVM으로부터 오는 배열의 길이는 ‘상수’이다.</li>
</ul>
<pre><code class="language-java">int[] arr1 = new int[5];
int[] arr2 = new int[5];

// arr1
for(int i=0; i&lt;5; i++){ // arr1의 길이가 바뀌면 같이 바뀌어야 한다.
    score[i] = i * 2;
}

// arr2
for(int i=0; i&lt;arr2.length; i++){ // arr2의 길이가 바껴도 건들지 않아도 된다.
    score[i] = i * 2;
}</code></pre>
<ul>
<li>for문으로 배열을 다루면, 반복횟수는 주로 <strong>‘0≤ i 〈배열의 길이’</strong>의 범위이다.</li>
<li>이를 <code>.length</code>를 사용하면 배열의 길이가 변해도, 반복문은 건들지 않는다.</li>
<li>프로그램이 실행되는 동안엔 한번 선언된 배열의 길이를 변경하는 건 불가능하다.</li>
<li>만약 공간이 부족할 시, 길이를 늘이려면 더 큰 배열에 기존 배열의 값을 복사하는 코드를 추가해둬야 한다.</li>
</ul>
<br/>

<h3 id="배열의-초기화">배열의 초기화</h3>
<ul>
<li>배열은 생성 시 타입의 기본값으로 자동 초기화된다. 그래서 원하는 값은 요소마다 직접 정해줘야 한다.</li>
<li>규칙이 있는 값이면 반복문을 사용하면 되지만, 그렇지 않다면 생성과 초기화를 동시에 할 수 있다.</li>
</ul>
<pre><code class="language-java">int[] score = new int[]{10, 12, 18, 15, 60}; // 길이가 5인 배열

// new int[]는 생략이 가능하다.
int[] score = {10, 12, 18, 15, 60}; // new int[] 생략 가능.

// 생략은 선언과 초기화가 동시에 할 때, 가능하다.
int[] score; 
score = new int[]{10, 12, 18, 15, 60}; 
score = {10, 12, 18, 15, 60}; // 에러, new int[] 생략 불가.

// 메서드의 인자값일 때도 new int[] 생략 불가
int result = add(new int[]{1, 2, 3});
int result = add({1, 2, 3}); // 에러.</code></pre>
<ul>
<li><code>{}</code> 안에 아무것도 넣지 않으면 길이가 0인 배열이 된다.<pre><code class="language-java">int[] score = new int[0];
int[] score = new int[]{};
int[] score = [];</code></pre>
</li>
</ul>
<br/>

<h4 id="배열의-출력">배열의 출력</h4>
<ul>
<li>출력 역시도 간단하다. 반복문으로 인덱스마다 출력해주면 된다.</li>
<li>한 번에 보고 싶다면 <code>Array.toString(배열명)</code>메서드를 사용하자. </li>
</ul>
<pre><code class="language-java">int[] score = {1, 2, 3, 4, 5};
System.out.println(“출력 : ” + Arrays.toString(score)); 
// 출력 : {1, 2, 3, 4, 5}</code></pre>
<ul>
<li>여기서 score를 그대로 출력하면, 배열이 아닌 배열의 주소가 16진수로 나온다.</li>
<li>물론 예외로 char배열은 그 자체가 String이기 때문에 순서대로 나온다.<pre><code class="language-java">char[] cArr = {‘A’, ‘B’, ‘C’};
System.out.println(cArr);
// ABC </code></pre>
</li>
</ul>
<br/>

<h3 id="배열의-복사">배열의 복사</h3>
<ul>
<li>한번 생성 된 배열의 길이를 변경하는 것은 어렵다. 따라서 더 큰 배열을 만들어서 요소들을 복사해야 한다.</li>
<li>복사 방식은 반복문 사용, <code>System.arraycopy()</code> 사용. 이렇게 두가지 방식이 있다.</li>
</ul>
<br/>

<p><strong>(1) for 반복문 사용</strong></p>
<pre><code class="language-java">int[] arr1 = new int[10]; // 기존 배열
int[] arr2 = new int[arr1.length * 2]; // arr1보다 길이가 2배인 배열


// 반복문으로 옮겨담기
for(int j = 0; j&lt;arr1.length; j++){
    arr2[j] = arr1[j]; 
}

// 참조변수의 주소를 옮기기
arr1 = arr2;</code></pre>
<ul>
<li>배열에 저장된 값을 옮겨 담고, 가리키는 배열의 주소를 바꿔준 것이다.</li>
<li>‘arr1’ 변수와 연결이 끊긴 배열은 JVM의 가비지 컬렉터가 메모리에서 자동으로 지운다.</li>
</ul>
<br/>

<p><strong>(2) System.arraycopy()</strong></p>
<ul>
<li>반복문과 달리 한번에 값을 복사한다. </li>
<li>요소마다 하나씩 접근하는 반복문보다 효율적인데, 이는 요소의 연속적 나열을 응용해서 처리하는 방식이다.</li>
<li>배열의 범위를 벗어날 경우, 에러가 발생한다.</li>
</ul>
<pre><code class="language-java">int[] arr1 = new int[10]; // 기존 배열
int[] arr2 = new int[arr1.length * 2]; // arr1보다 길이가 2배인 배열

System.arraycopy(arr1, 0, arr2, 0, arr1.length);
/* arr1의 인덱스 0포함 arr1.length개의 데이터를 
   인덱스 arr2의 인덱스 0부터 복사해서 저장해라.
*/</code></pre>
<br/>

<h3 id="배열의-활용">배열의 활용</h3>
<ul>
<li>배열을 이용해서 실습을 진행해봤다. 대략 7개가 수록되어 있지만 적당하게 간단한 것만 진행해 봤다.</li>
</ul>
<br/>


<h4 id="1-최대값-최소값">(1) 최대값, 최소값</h4>
<pre><code class="language-java">// score 안의 수는 0~1000사이의 수다.
int[] score = {100, 21, 657, 15, 132, 544, 221, 687, 54};

// 사실 인덱스 0으로 초기화 시키는게 낫다.
int max = -1; 
int min = 1001;

for(int i=0; i&lt;score.length; i++){
    if(score[i]&gt;max){
        max = score[i];
    }
    if(score[i]&lt;min){
        min = score[i];
    }
}
System.out.println(&quot;최대값 :&quot; + max); // 최대값 : 687
System.out.println(&quot;최소값 :&quot; + min); // 최소값 : 15</code></pre>
<ul>
<li>배열 안의 값을 모르거나, 입력을 받아서 유동적일 때는 저렇게 최소값과 최대값을 설정하면 비교가 가능하다.</li>
</ul>
<br/>

<h4 id="2-로또-번호-뽑기">(2) 로또 번호 뽑기</h4>
<pre><code class="language-java">int[] ball new int[45]; // 1~45를 저장할 배열

// 배열 초기화 1~45
for(int i=0; i &lt; ball.length; i++){
    ball[i] = i+1;
}

int temp = 0; // 백업 공간
int j = 0; // 임의 인덱스 값을 저장할 변수

// 섞기
for(int i=0; i &lt; 6; i++) {
    j = (int)(Math.random()* 45); // 인덱스는 0~44이다.
    temp = ball[i];
    ball[i] = ball[j];
    ball[j] = temp;
}

// 번호 출력
for(int i=0; i &lt; 6; i++){
    System.out.print(ball[i] + “ ”);
}</code></pre>
<ul>
<li>반복횟수를 높여서 섞는 방식도 있지만, 그것 역시 특정 위치의 인덱스만 뽑는 것이다.</li>
<li>따라서 <code>Math.random()</code> 으로 인덱스 값만 뽑아서 교환한 다음 원하는 개수만 뽑아도 섞여있다.</li>
</ul>
<br/>


<h4 id="3-버블-정렬">(3) 버블 정렬</h4>
<pre><code class="language-java">int[] numArr =  {1, 4, 2, 6, 7, 8, 12, 34};

// 총 반복(회차)
for(int i=0; i&lt;numArr.length-1; i++){

    // 요소간 비교
    for (int j=0;j&lt;numArr.length-1-i; j++) {
        if(numArr[j] &gt; numArr[j+1]) { // 왼쪽의 값이 크면 서로 바꾼다.
            int tmp = numArr[j];
            numArr[j] = numArr[j+1];
            numArr[j+1] = tmp;
        }
    }
}</code></pre>
<ul>
<li>버블 정렬 알고리즘은 요소의 값을 크기에 따라서 정렬한다. 단, 한번에 모두 정렬되는 것이 아니다.</li>
<li>총 정렬해야 하는 수(반복횟수)는 ‘길이-1’로, 마지막 1개는 자동으로 정렬되기 때문이다.</li>
<li>안쪽 반복은 첫 인덱스(0)부터 마지막 인덱스(길이-1)까지의 요소에 접근해서 2개를 동시 비교한다.</li>
<li>비교 시, 왼쪽 값이 우측 값보다 크면 서로 교환한다.</li>
<li>결과적으로 한번 반복할 때마다 하나의 값은 정렬이 끝난다. 따라서 비교 대상도 한 회차마다 한 개씩 줄어든다. <strong>‘길이-1 – 회차수&#39;</strong></li>
<li>요소가 비교가 길이-1인 이유는 두 값을 동시 비교하기 때문이다.</li>
</ul>
<br/>


<p><strong>￮ 요약</strong></p>
<ul>
<li>배열은 같은 타입의 변수를 하나로 묶어둔 것이다.</li>
<li>배열을 가리키는 참조변수에는 메모리 상에 있는 변수의 주소가 저장된다.</li>
<li>요소는 배열을 구성하며, 각 요소에는 순서(인덱스)가 있다. 인덱스는 0부터 시작한다.</li>
<li>배열 생성 시 타입의 기본값으로 자동 초기화된다..</li>
<li>배열의 선언과 메서드 호출로 사용시에는 <code>new</code>를 생략할 수 없다.</li>
<li>char배열은 출력하면 문자열로 나온다.</li>
<li>동적으로 한번 생성된 배열의 길이는 바꿀 수 없다. 컬렉션을 쓰거나 아니면 더 큰 배열을 만들어서 복사해줘야 한다.</li>
<li>반복문만 있으면 배열로 정렬, 섞기 등 다양한 것을 할 수 있다.</li>
</ul>
<hr>
<h2 id="2-string배열">2. String배열</h2>
<br/>

<h3 id="string배열의-선언과-생성">String배열의 선언과 생성</h3>
<ul>
<li>String은 char의 배열이다. 거기에 여러 기능을 추가한게 String 클래스인데, String타입 역시 배열로 다룰 수 있다.</li>
</ul>
<pre><code class="language-java">String[] name = new String[3];
name[0] = “kim”;
name[1] = “park”;
name[2] = “Lee”;</code></pre>
<ul>
<li>이렇게 되면 3개의 문자열이 저장되는 문자열 배열이 만들어진다.</li>
<li>배열의 배열로서, 각 요소에는 문자열의 주소가 저장된다. (객체 배열)</li>
</ul>
<p><img src="https://velog.velcdn.com/images/ho_c/post/c12c49b3-d019-4880-9cae-4b4288792756/image.jpg" alt=""></p>
<br/>

<ul>
<li>참조변수의 기본값은 null이므로, null로 초기화 된다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/ho_c/post/dd59be30-2838-4843-8e92-03290cb2d9d4/image.jpg" alt=""></p>
<br/>





<h4 id="초기화">초기화</h4>
<ul>
<li>초기화 역시, 일반 배열이랑 별반 다르지 않다.</li>
</ul>
<pre><code class="language-java">String[] name1 = new String[]{“kim”, “park”, “Lee”};
String[] name2 = {“kim”, “park”, “Lee”}
String[] name3 = new String[3];
name3[0] = “kim”;
name3[1] = “park”;
name3[2] = “Lee”;</code></pre>
<br/>

<h3 id="char배열과-string클래스">char배열과 String클래스</h3>
<ul>
<li>문자열은 char의 배열이다. 곧 문자의 배열이다. 하지만 이게 JAVA에서 String은 아니다.</li>
<li>자바의 String은 문자배열에 이를 다루는 다양한 기능을 묶어놓은 “클래스”이다.</li>
<li>이 둘의 차이는 char배열은 내용 수정이 가능하지만, String클래스는 변경이 불가능하다는 것이다.</li>
<li>즉, 배열의 요소값을 바꾸는 것과 객체에 연결된 주소를 바꾸는 건 서로 다른 문제다.</li>
</ul>
<br/>

<h4 id="string클래스의-주요-메서드">String클래스의 주요 메서드</h4>
<p><img src="https://velog.velcdn.com/images/ho_c/post/b2539403-6889-4576-b323-983ac3140bc7/image.jpg" alt=""></p>
<br/>


<h4 id="번외-커맨드-라인을-통해-입력받기">번외. 커맨드 라인을 통해 입력받기</h4>
<ul>
<li>Scanner 클래스 안쓰고도 커맨드라인으로 값을 입력 받을 수 있다.</li>
<li>커맨드라인으로 입력 시, main 메서드의 인자값으로 들어간다.</li>
<li>공백이 있는 단어는 <code>“”</code> 으로 묶어준다.</li>
<li>입력한 문자열만큼 배열이 생성되어 다룰 수 있다.</li>
</ul>
<pre><code>C:\jdk1.8\work\ch5&gt;java ArrayEx16 abc 123 &quot;Hello world&quot;</code></pre><pre><code class="language-java">public static void main(String[] args){
        System.out.println(&quot;매개변수의 개수 :&quot;+args.length);
        for(int i=0;i&lt; args.length;i++){
            System.out.println(&quot;args[&quot; + i + &quot;] = \&quot;&quot;+ args[i] + &quot;\&quot;&quot;);
        }
}

/* 출력
매개변수의 개수 : 3
args[0] = &quot;abc&quot;
args[1] &quot;123&quot;
args[2] = &quot;Hello world&quot;
*/</code></pre>
<hr>
<h2 id="3-다차원-배열">3. 다차원 배열</h2>
<ul>
<li>배열은 1차원뿐만 아니라, 앞선 String배열처럼 “배열의 배열” 형태로 다룰 수 있다.</li>
<li>이를 다차원 배열이라고 하며 이번 파트에선 “2차원 배열”을 주로 사용한 것이다.</li>
</ul>
<br/>

<h3 id="2차원-배열의-선언과-인덱스">2차원 배열의 선언과 인덱스</h3>
<h4 id="2차원-배열-선언">2차원 배열 선언</h4>
<ul>
<li><p>선언방법은 1차원처럼 배열표시“[]”가 하나 더 들어간다.</p>
<pre><code class="language-java">int[][] score; // 타입[][] 변수명
int score[][]; // 타입 변수명[][]
int[] score[]; // 타입[] 변수명[] </code></pre>
</li>
<li><p>변수가 선언되었다면, 실제 배열생성은 다음과 같다.</p>
<pre><code class="language-java">int[][] score =  new int[4][3] // 4(행-세로) X 3(열-가로) 배열 생성</code></pre>
</li>
<li><p>구조를 보면 다음과 같고, 생성과 함께 int의 기본값 0으로 초기화 된다.</p>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/ho_c/post/20a28b2b-5767-49e2-a654-6f44e3468d9b/image.jpg" alt=""></p>
<br/>

<h4 id="2차원-배열의-인덱스">2차원 배열의 인덱스</h4>
<ul>
<li>이번에는 배열의 요소에 접근 방법을 알아보자.</li>
<li>일단 2차워 배열 역시, 인덱스는 0~길이-1이까지의 범위이다.</li>
<li>따라서 앞서 생성한 score[][]으로 접근을 하면 다음과 같다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/ho_c/post/d7d1b46a-724b-4d05-9eb1-c41b09db1478/image.jpg" alt=""></p>
<ul>
<li>위 사진처럼 2차원을 배열은 배열 안에 배열을 연결한 방식으로 구현이 되어져 있다.</li>
<li>여기서 데이터를 추출하거나, 변경하고 싶다면 해당 인덱스를 따라서 변경하면 된다.</li>
</ul>
<br/>


<h3 id="2차원-배열의-초기화">2차원 배열의 초기화</h3>
<ul>
<li>2차원 배열도 생성과 동시에 원하는 값으로 초기화가 가능하다.</li>
</ul>
<pre><code class="language-java">int[][] score = {
        (100, 100, 100},
        (20, 20, 20},
        (30, 30, 30),
        (40, 40, 40),
        (50, 50, 50)
    };</code></pre>
<p><img src="https://velog.velcdn.com/images/ho_c/post/24140978-68cf-4943-9f85-20a4c3a8944d/image.jpg" alt=""></p>
<ul>
<li><p>배열의 배열이기 때문에, <code>score.length ==  5</code> 이다. 즉, score은 배열 1개의 주소를 갖고 있다.</p>
</li>
<li><p>그리고 해당 배열의 각 요소는 또 다른 배열의 주소이므로 ‘배열 주소의 배열’이다.</p>
</li>
<li><p>즉, <code>score[?]</code> 자체가 배열 하나의 참조변수라고 이해하는 것이 편하다. 
따라서 <code>score[0].length == 3</code>이 된다.</p>
</li>
<li><p>괄호 외에도 이중 반복문으로 2차원 배열의 초기화가 가능하다.</p>
<pre><code class="language-java">for(int x=0; x&lt;score.length; x++){
  for(int y=0; y&lt;score[i].length; y++){
      score[x][y] = 0; // 모든 요소를 0으로 초기화
  }
}</code></pre>
</li>
<li><p>향상된 for문으로 다차원 배열을 처리 시에는 값의 변경은 불가능하다. 또한 score의 각 요소는 int[] 배열의 주소를 타입으로 가지기 때문에 int타입으로 사용할 시, 에러가 일어난다.</p>
<pre><code class="language-java">for(int[] tmp : score){ // score의 각 요소는 int[]배열을 가리킨다.
  for(int i: tmp){ // tmp의 각 요소는 int이다.
      System.out.println(i);
  }
}</code></pre>
</li>
</ul>
<br/>

<h3 id="가변-배열">가변 배열</h3>
<ul>
<li>배열에 연결된 다른 배열들의 길이가 모두 동일할 필요가 없다. 즉, 사각형의 형태를 가진 배열이 아니어도 충분히 다룰 수 있다.</li>
<li>이를 가변 배열이라고 하는데, 가변 배열은 두 번째 차원의 길이를 지정하지 않으면 된다.<pre><code class="language-java">int[][] arr = new int[5][]; // 두 번째 차원의 길이는 지정하지 않았다.
</code></pre>
</li>
</ul>
<p>// arr의 요소마다 길이가 다른 배열을 생성해서 연결해준다.
arr[0] = new int[4];
arr[1] = new int[1];
arr[2] = new int[3];
arr[3] = new int[2];
arr[4] = new int[3];</p>
<pre><code>


![](https://velog.velcdn.com/images/ho_c/post/a3aaa5c0-a056-43c8-9fc0-07e7bbe28e0b/image.jpg)




- 배열마다 길이가 다르더라도, 초기화는 `{}` 을 이용해서 생성과 함께 할 수 있다.
```java
int[][] score = {
            {100, 100, 100, 100},
            {20, 20, 20},
            {40, 40},
            {50, 50, 50}
        };</code></pre><br/>


<p><strong>￮ 요약</strong></p>
<ul>
<li>“배열의 배열”의 형태로 다차원 형태의 배열을 다룰 수 있다.</li>
<li>다차원 배열을 사용할 시, 배열의 요소는 다른 “배열의 주소”이다.</li>
<li>배열의 생성방법은 1차원과 다르지 않다. 그렇기 <code>new</code>의 생략도 마찬가지이다.</li>
<li>다차원 배열의 초기화 역시, {}을 이용해서 가능하다.</li>
</ul>
<br/>

<hr>
<blockquote>
<p>도움이 되셨다면 <strong>&#39;좋아요&#39;</strong> 부탁드립니다 :)</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[Ch04. 조건문과 반복문]]></title>
            <link>https://velog.io/@ho_c/Ch04.-%EC%A1%B0%EA%B1%B4%EB%AC%B8%EA%B3%BC-%EB%B0%98%EB%B3%B5%EB%AC%B8</link>
            <guid>https://velog.io/@ho_c/Ch04.-%EC%A1%B0%EA%B1%B4%EB%AC%B8%EA%B3%BC-%EB%B0%98%EB%B3%B5%EB%AC%B8</guid>
            <pubDate>Tue, 28 Mar 2023 11:03:01 GMT</pubDate>
            <description><![CDATA[<h1 id="들어가는-말">들어가는 말</h1>
<ul>
<li><p>컴퓨터는 소스코드를 위에서 아래로 순서대로 실행하고 이를 <strong>‘흐름’</strong>이라고 한다. 
하지만 실제로 코드를 짜고 애플리케이션을 만들다 보면, 한 방향으로 코드가 진행될 경우 흐름이 복잡해져 유지보수도 어려워진다.</p>
</li>
<li><p>또 컴퓨터의 매력이 반복작업을 매우 빨리 처리하는 것인데, 그럼 같은 코드를 100번씩이나 중복해서 넣는 것도 비효율적이다. 
그래서 이런 한 방향의 흐름을 원하는 곳으로, 또는 계속 순환시키도록 하는 문장을 <strong>‘제어문’</strong>이라 한다.</p>
</li>
</ul>
<hr>
<h2 id="1-조건문-if-switch">1. 조건문: if, switch</h2>
<ul>
<li>조건문은 <code>조건식 + {}(블록)</code>으로 구성되고, 블록 안에는 조건식의 연산결과에 따라 실행될 문장들이 들어간다.</li>
</ul>
<blockquote>
<p>조건문은 if, switch문 두 가지인데, 처리할 조건이 많을수록 switch문이 깔쌈하지만 대신 if문보다 제약이 많다.</p>
</blockquote>
<br/>

<h3 id="if문">if문</h3>
<ul>
<li>가장 기본적인 조건문으로, (조건식)의 결과가 <strong>‘참’</strong>(true)이면 {}안의 코드를 실행한다.</li>
</ul>
<pre><code class="language-java">if(조건식){
    // true일 경우 실행.    
}</code></pre>
<h4 id="조건식">조건식</h4>
<ul>
<li><code>(조건식)</code>은 비교연산자(&lt;= &gt;= ==)와 논리연산자(&amp;&amp; ||)의 조합으로 구성된다.</li>
</ul>
<h4 id="블록-">블록 {}</h4>
<ul>
<li><code>{}</code>을 이용하면 많은 코드를 하나의 영역(scope)로 묶어준다. 
블록이 시작되면 들여쓰기로 해당 위치를 표시해주는 것이 좋다.</li>
</ul>
<p>여기서 조건문의 블록 내 실행 코드가 한 줄이면, 블록을 생략하고 한 줄로 쓸 수 있다.</p>
<pre><code class="language-java">if(score &gt; 60)
System.out.println(&quot;합격입니다.&quot;); // 개인적으로 비추.</code></pre>
<br/>

<h3 id="if-else문">if-else문</h3>
<ul>
<li>if-else는 if문의 변형으로, else블록이 마지막에 추가된다. </li>
<li>else블록은 포함된 모든 조건식의 결과가 거짓일 때, 실행된다. </li>
<li>else 역시 한 줄일 경우 생략이 가능하다.</li>
</ul>
<blockquote>
<p>다만, 중첩 if문에서 생략해서 쓸 경우 가장 가까운 if문의 블록이 된다.</p>
</blockquote>
<pre><code class="language-java">if(조건식){
    System.out.println(&quot;진실입니다.&quot;): // 조건식이 참일 때.
} else { 
    System.out.println(&quot;거짓입니다.&quot;); //조건식이 거짓일 때.
}</code></pre>
<br/>

<h4 id="상반된-조건일-때-사용하면-좋다">상반된 조건일 때 사용하면 좋다.</h4>
<ul>
<li>else문이 존재하면, 해당 if문은 무조건 한번은 실행되게 된다. 
특히 조건이 상반되는 경우, else문을 사용하면 코드를 효율적으로 작성할 수 있다.<pre><code class="language-java">// 상반된 조건
if(input == 0){
  System.out.println(&quot;0입니다.&quot;);
}
if(input != 0){
  System.out.println(&quot;00이 아닙니다.&quot;);
}
</code></pre>
</li>
</ul>
<p>// if-else로 변경
if(input == 0){
    System.out.println(&quot;0입니다.&quot;);
}
else{
    System.out.println(&quot;0이 아닙니다.&quot;);
}</p>
<pre><code>
&lt;br/&gt;


### if-else if문
- if문이 하나면 조건이 하나, if-else는 둘 중 하나가 실행된다. 즉, 최대조건이 2개밖에 작성을 못한다.

&gt; 그래서 하나의 조건문에 여러 조건을 넣을 경우 else if블록을 추가하면 된다.

```java
if(조건 1){
    // 실행 1
} else if(조건 2){
    // 실행 2
} else if(조건 3){
    // 실행 3
} else {
    // 실행 4 : 1,2,3이 거짓일 때 실행
}</code></pre><p>여기서 재밌는 건, 이전의 평가한 조건을 기반으로 평가가 된다는 것이다.</p>
<pre><code class="language-java">if (score &gt;= 90) (
    grade = &#39;A&#39;;
} else if (80 &lt;= score &amp;&amp; score &lt; 90) { // 80 ≤ score 90
    grade = &#39;B&#39;;
} else if (70 &lt;= score score &lt; 80) { // 70 ≤score 80
    grade = &#39;C&#39;;
} else { // score &lt; 70
    grade = &#39;D&#39;;
}

// 이전 조건을 참조
if (score &gt;= 90) (
    grade = &#39;A&#39;;
} else if (80 &lt;= score) { // 80 ≤ score 90
    grade = &#39;B&#39;;
} else if (70 &lt;= score ) {// 70 ≤score 80
    grade = &#39;C&#39;;
} else { // score &lt; 70
    grade = &#39;D&#39;;
}</code></pre>
<p>위 코드에서 보듯이, if문은 첫 번째부터 순서대로 조건을 검사한다. 따라서 이미 기존에 검사했던 결과를 참조함으로써 중복 조건을 최대한 배제한다.</p>
<br/>

<p><strong>￮ 요약</strong></p>
<ul>
<li>첫 번째 조건부터 순서대로 비교된다.</li>
<li>참인 조건을 만나면 해당 블록의 동작을 수행 후 if문 전체를 벗어난다.</li>
<li>else if의 조건은 이전의 평가된 조건식의 결과를 참고 한다.</li>
<li>if문을 여러개로 나눈다고 else if문은 아니다. 그래서 앞서 말한 혜택은 else if일때만 가능하다.</li>
</ul>
<br/>

<h3 id="중첩-if문">중첩 if문</h3>
<ul>
<li>if 조건문 안에 새로운 조건을 계속 중첩해서 만드는 것을 말한다. 
중첩 횟수는 제한이 없지만 많을수록 가독성은 떨어진다.</li>
</ul>
<pre><code class="language-java">if(조건 1){
    // 조건 1 : true일 때만 if문 비교
    if(조건 2){
        // 조건 1, 2 모두 true
    } else {
        // 조건 1 : true, 조건 2 : false
    }

} else {
    // 조건 1 : false
}</code></pre>
<br/>


<h3 id="switch문">switch문</h3>
<ul>
<li>if문은 값은 참과 거짓으로 판단해서 코드를 실행한다. 
그래서 조건이 추가될수록 <code>else-if</code> 만 많아진다.
이와 달리 switch문은 하나의 조건식에 여러 개의 값을 걸어둘 수 있다. 
다만 if문과 달리 제약 조건이 있다.</li>
</ul>
<br/>

<h4 id="switch문의-동작-순서">switch문의 동작 순서</h4>
<blockquote>
<p>① 조건식 연산 → ② 결과와 일치하는 case문으로 이동 → ③ 문장 수행 → ④ break문 or switch문의 끝을 만나면 종료</p>
</blockquote>
<pre><code class="language-java">switch (조건식)  {
    case 값1 :
        // 수행문 1
        // 수행문 2
        break; // switch문 이탈
    case 값2 :
        // 수행문 3
        // 수행문 4
        break; // switch문 이탈

    default : // else문의 역할

}</code></pre>
<p>여기서 주의할 점은 break문의 위치이다. 만약 break문을 작성해주지 않는다면, 최상단 case문에서 시작할 경우 switch문의 끝에 닿을 때까지 포함된 모든 수행문을 실행할 것이다.</p>
<br/>

<h4 id="switch문의-제약-조건">switch문의 제약 조건</h4>
<ul>
<li>조건식의 결과값이 무조건 정수여야 한다. (문자열도 가능)</li>
<li>따라서 case 역시 정수와 상수만 가능하다. (중복 X)</li>
</ul>
<br/>

<h4 id="switch문의-중첩">switch문의 중첩</h4>
<ul>
<li>if문처럼 switch문 안에 switch문도 가능하다. 근데 막상 보면 if문이 더 간다해 보인다.<pre><code class="language-java">switch (조건식1)(
  case &#39;1&#39;: case &#39;3&#39;:
      switch (조건식2){
          case &#39;a&#39;:
              // 수행문 a
              break;
          case &#39;b&#39;:
              // 수행문 b
      }
      break; // case 1,3의 break
  case &#39;2&#39;: case &#39;4&#39;:
      switch (조건식3){
          case &#39;c&#39;:
              // 수행문 c
              break;
          case &#39;d&#39;: 
              // 수행문 d
      }
}</code></pre>
</li>
</ul>
<br/>

<p><strong>￮ 요약</strong></p>
<ul>
<li>하나의 조건식으로 여러 값을 사용할 때, switch문을 사용한다.</li>
<li>조건의 결과는 정수 or 문자열만 가능하다.</li>
<li>case문의 값은 정수, 상수만 가능하고 중복은 불가능하다.</li>
<li>default문은 else문과 같은 역할이다. 마지막에 위치해서 break는 안 써도 된다.</li>
<li>break를 case문 사이마다 작성하지 않으면 그 사이에 있는 수행문이 다 실행된다.</li>
</ul>
<hr>
<h2 id="2-반복문-for-while-do-while">2. 반복문: for, while, do-while</h2>
<p>사람은 반복하는 작업을 지루해하지만, 컴퓨터는 반복 작업을 하기 위해 존재한다. 그래서 컴퓨터에게 반복작업을 시키기 위해 ‘반복문’을 배워보자.</p>
<h3 id="for문">for문</h3>
<ul>
<li>for문은 주로 반복 작업의 횟수가 적당할 때 사용한다. 
구조는 초기화, 조건식, 증감식, 블록{}으로 이루어져 있다.<pre><code class="language-java">for(초기화; 조건식; 증감식){
  // 수행문;
}
</code></pre>
</li>
</ul>
<p>for(int i = 1; i&lt;=10; i++){
    System.out.println(“공부하기 싫다.”);
}</p>
<pre><code>
해당 for문을 실행하면 선언된 변수 i에 1이 저장된 후, 조건식이 평가된다. 결과가 true이면 수행문을 실행하고, 증감식이 실행되면 1→2로 변수 i에 저장된 값이 올라간다.

&lt;br/&gt;

#### for문의 구조와 수행 순서

- 앞 전의 코드에서 본 것처럼 for문은 초기화, 조건식, 증감식, 블록으로 이루어져 있다.
- 조건식이 참일 때, 블록 안의 코드를 수행, 거짓이 되면 이탈한다.

**① 초기화**
- 반복문에서 사용할 변수를 선언하고, 초기화한다.
- for문에서 제일 먼저 실행되며, 처음 한번만 수행된다.
- 변수가 여러 개이면 `,` 으로 구분한다. 

```java
for (int i=1;i&lt;= 10;i++) { ... } 
for (int i=1,j=0;i&lt;=10;i++) { ... } </code></pre><p><strong>② 조건식</strong></p>
<ul>
<li>조건의 값이 참이면 반복하고, 거짓이면 반복을 멈추고 for문을 이탈한다.</li>
<li>배열을 다룰 때는 배열의 길이를 활용한다.</li>
</ul>
<p><strong>③ 증감식</strong></p>
<ul>
<li>선언된 변수의 값을 증감하는 식이다. 이로 인해서 반복의 끝낼 수 있다.</li>
<li>증감연산자, 복합대입연산자를 주로 이용한다.</li>
<li><code>,</code>를 이용해서 여러 변수를 증감할 수 있다.</li>
</ul>
<p><strong>￮ 요약</strong></p>
<ul>
<li>반복횟수를 알 때, 쓰기 좋다.</li>
<li>변수와 증감식은 여러개가 될 수 있다.</li>
<li>조건식이 거짓이 되면 반복이 종료된다.</li>
<li>수행문이 한 줄이면, {}을 생략할 수 있다.</li>
<li>초기화, 조건식, 증감식을 모두 생략하면 무한반복문이 된다.</li>
</ul>
<br/>

<h3 id="중첩-for문">중첩 for문</h3>
<ul>
<li>조건문처럼 반복문도 중첩이 가능하다. 일종의 멀티 플레이가 가능하다고 생각하는게 편하다.
중첩의 횟수제한은 없지만, 이 역시 여러번 중첩되면 가독성이 떨어진다.</li>
</ul>
<pre><code class="language-java">// 구구단 예제
for(int x = 1; x &lt; 10; x++;){
    for(int y = 1; y &lt; 10; y++){
        System.out.printf(“%d * %d = %d%n”, x,y,x*y);
    }
}</code></pre>
<br/>

<h3 id="향상된-for문">향상된 for문</h3>
<ul>
<li>애플리케이션에 객체 배열 안에 있는 DTO의 정보들을 다룰 때, 주로 사용하는 for문이다.</li>
<li>쉽게 말해서 값은 타입의 객체를 대량으로 다룰 때, 변수 한 개만으로 반복작업을 할 수 있게 해주는 for문이다.</li>
</ul>
<pre><code class="language-java">for (타입 변수명 : 배열 또는 컬렉션){
    // 수행문
}

int[] arr = […];
for (int a : arr){
    // 수행문
}</code></pre>
<blockquote>
<p>배열이나 컬렉션 안에 저장된 값들을 그 값에 맞는 타입의 변수에 연결해서 다루는 것이다. 따라서 꺼내는 값의 타입과 변수의 타입이 일치해야 된다.</p>
</blockquote>
<br/>

<h3 id="while문">while문</h3>
<ul>
<li>반복문의 원조는 while문이다. 그래서 for문은 언제든지 while문으로 while 역시 for문으로 변경해서 사용할 수 있다.</li>
</ul>
<pre><code class="language-java">while(조건식){
    // 수행문
}</code></pre>
<br/>

<h4 id="for문과-while문의-비교">for문과 while문의 비교</h4>
<blockquote>
<p><strong>for문과 while의 차이는 뭘까?</strong></p>
</blockquote>
<ul>
<li>일단 둘 다 같은 작업을 코드로 만들 수 있다. 다만 while문은 for문과 달리 조건식이 “거짓”이 될 때까지 반복한다.</li>
</ul>
<pre><code class="language-java">// for
for(int i=1;i&lt;=10;i++) {
    System.out.println(i);
}

// while
int i = 1;
while(i&lt;=10){
    System.out.println(i);
    i++;         
}  </code></pre>
<ul>
<li>또 예를 들어 특정 조건일 때, 무한 반복을 하게 되는 코드의 경우도 while문으로 변환이 가능하다.</li>
</ul>
<pre><code class="language-java"> if(i%5==0){
        for(;;)
}
while(i%5==0){

}</code></pre>
<br/>

<p><strong>while문의 조건식은 생략이 안된다.</strong></p>
<ul>
<li>for문은 조건식 생략으로 무한루프를 만들기도 하지만, while은 조건식이 없으면 안된다. 
그래서 무한루프를 만들려면 조건식이 항상 참이어야 한다.</li>
</ul>
<pre><code class="language-java">while(true){
    // 수행문
}</code></pre>
<blockquote>
<p>다만 무한 반복문의 경우, 끝내지만 않으면 계속 돌아가기 때문에 특정 조건을 이용해서 멈추는 break문을 만들어줘야 한다.</p>
</blockquote>
<br/> 


<h3 id="do-while문">do-while문</h3>
<ul>
<li>do-while은 while문의 변형으로 차이점은 최소한 한번은 수행문이 반복된다는 걸 보장한다는 것이다.</li>
</ul>
<pre><code class="language-java">do {
    // 조건식의 연산결과가 참일 때 수행.
} while (조건식); // 여기에 ;이 붙음</code></pre>
<blockquote>
<p>while은 조건식이 거짓이면, 수행되지 않는다. 
반대로 do-while은 do절의 수행문이 먼저 실행되고 조건식이 평가되므로 최소한 한번은 수행된다.</p>
</blockquote>
<br/>

<p><strong>￮ 요약</strong></p>
<ul>
<li>while문은 조건이 참인 동안 반복된다.</li>
<li>while의 조건식은 생략을 할 수 없다.</li>
<li>do-while은 수행문이 먼저 실행되고 조건식이 평가된다.</li>
</ul>
<br/>


<h3 id="break문">break문</h3>
<ul>
<li>break라는 예약어를 만나면 자신이 포함된 조건문, 반복문을 이탈한다. 
한마디로 코드의 진행이 멈추게 된다.</li>
</ul>
<pre><code class="language-java">int x = 0;

while(true){
    x++:
    if (x&gt;2000){ // x의 값이 2000을 넘기면 실행
        break; // while문을 벗어난다.
    }
}</code></pre>
<br/>

<h3 id="continue문">continue문</h3>
<ul>
<li><p>break가 반복을 멈추는 것이라면, continue는 반복을 뛰어넘는다. 
즉 skip의 역할을 하는데, 해당 반복문의 끝으로 이동해서 다음 반복으로 넘어간다.</p>
</li>
<li><p>break와 달리 반복문 자체를 벗어나지 않는다.</p>
</li>
</ul>
<pre><code class="language-java">for (int i=0; i&lt;=10; i++){
    if (i%3==0){
        continue; // 3의 배수일 때마다 반복을 넘긴다.
    }
    System.out.println(i); // 1, 2, 4, 5, 7, 8, 10
} ← 여기로 이동</code></pre>
<br/>


<h3 id="이름-붙은-반복문">이름 붙은 반복문</h3>
<ul>
<li>break의 특징은 가장 가까운 하나의 반복문만 벗어날 수 있다.
그래서 중첩 반복문 같은 경우에는 한 번에 끝내려면 break를 중첩해서 사용해야된다,</li>
<li>그치만 그럼 귀찮으니까, 반복문에 이름을 정해줘서 해당 반복문을 끝내거나, 뛰어넘는 방식이 존재한다.</li>
</ul>
<pre><code class="language-java">Loop1 : for(int i=2; i&lt;=9; i++) {
    for (int j=1;j&lt;=9;j++) {
        if(j==5){
            break Loop1;
            // break; : j 반복문만 끝냄.
        }
        System.out.println(i+&quot;*&quot;+j+&quot;=&quot;+ i*j);
    } 
} // Loop1의 끝</code></pre>
<blockquote>
<p>만약 반복문의 이름을 붙이지 않았다면, break는 j가 5일 때마다 멈춰서, 구구단 2단부터 9단까지 4까지만 출력이 될 것이다.
하지만 이름을 붙였기 때문에 <code>2*1, 2*2, 2*3, 2*4</code>만 출력된다.</p>
</blockquote>
<br/>

<p><strong>￮ 요약</strong></p>
<ul>
<li>break문은 가장 가까운 단 하나의 반복문을 이탈한다.</li>
<li>continue는 해당 반복문의 끝으로 이동해서 다음 반복으로 넘어간다.</li>
<li>반복문에 이름을 붙여서 break, continue를 사용하면 명명된 반복을 이탈하거나, 넘어간다.</li>
<li>반복문에 이름을 붙이는 건, while의 무한반복문에서 주로 사용한다. (전체 종료)</li>
</ul>
<br/>

<hr>
<blockquote>
<p>도움이 되셨다면 <strong>&#39;좋아요&#39;</strong> 부탁드립니다 :)</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[Ch03. 연산자]]></title>
            <link>https://velog.io/@ho_c/Ch03.-%EC%97%B0%EC%82%B0%EC%9E%90</link>
            <guid>https://velog.io/@ho_c/Ch03.-%EC%97%B0%EC%82%B0%EC%9E%90</guid>
            <pubDate>Sat, 25 Mar 2023 08:05:20 GMT</pubDate>
            <description><![CDATA[<h1 id="들어가는-말">들어가는 말</h1>
<ul>
<li>‘변수와 자료형’에선 데이터를 컴퓨터에 저장하고, 컴퓨터는 어떤 방식으로 데이터를 처리하는지에 대해서 알아봤다.
이번 ‘연산자’ 파트에선 저장된 데이터를 갖고 원하는 결과를 얻는 방법을 알아보자.</li>
</ul>
<hr>
<br/>

<h2 id="1-연산자">1. 연산자</h2>
<pre><code>: 연산을 수행하는 기호</code></pre><p><code>+ - / *</code> 와 같은 사칙연산을 위한 연산자부터, <code>=</code> 과 같은 다양한 연산자가 자바에 존재한다.</p>
<br/>

<h3 id="연산자와-피연산자">연산자와 피연산자</h3>
<ul>
<li>연산자 : 연산 수행 기호 ex) +, -, == 등</li>
<li>피연산자 : 연산의 대상 ex) 변수, 상수, 리터럴, 수식</li>
</ul>
<p>$x + 3$</p>
<p>위 수식에서 ‘+’는  2개의 피연산자 $x$, $3$을 더한 결과를 반환한다. 이를 보면 연산자는 피연산자로 연산을 수행하면, 그 후 <strong>“결과값을 반환한다”</strong>는 것이다.</p>
<br/>

<h3 id="식式과-대입연산자">식(式)과 대입연산자</h3>
<pre><code>식 : 연산자와 피연산자의 조합으로 계산하고자 하는 걸 표현하는 것.</code></pre><p>식을 계산해서 결과를 얻는다는 걸 ‘평가’라고 한다. 그리 중요하지 않으니 그냥 넘어가도 된다.</p>
<pre><code class="language-java">4 * x * 3;</code></pre>
<p>위에서 x=5 일 경우, 23이라는 값이 평가되겠지만 반환된 결과는 사라지고 만다. 그래서 대입연산자 <code>=</code>를 이용해서 값을 변수에 저장해줘야, 평가된 값을 사용할 수 있다.</p>
<pre><code class="language-java">x = 5;
y = 4 * x * 3;</code></pre>
<br/>

<h3 id="연산자의-종류">연산자의 종류</h3>
<ul>
<li>연산자의 종류는 용도에 따라 크게 ‘산수, 비교, 논리, 대입’로 나눌 수 있다. 
물론 피연산자의 수에 따라서도 나눌 수 있다는 것만 알아주자.
꽤 양이 많은 것 같지만, 쓰다보면 알게 되니 달달 외워둘 필요는 없다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/ho_c/post/c540ecf7-2c39-41a1-8a86-c626b726a632/image.jpg" alt=""></p>
<h4 id="피연산자의-개수로-의한-분류">피연산자의 개수로 의한 분류</h4>
<ul>
<li><p>앞서 말했듯 연산자는 피연산자의 개수로도 분류할 수 있다. 
피연산자가 1개면 단항, 2개면 이항, 3개면 삼항 연산자라고 한다.</p>
</li>
<li><p>대부분은 이항이니 외울 필요는 없고, 삼항은 표를 보면 나오듯 <code>? :</code>한 개 뿐이다. 
단항은 <code>=, ++, --, -</code> 등이 있는데 여기서 <code>-</code>는 산술 연산 시에는 뺄셈 연산자이지만, 
단항으로 쓰이게 된다면 음수를 나타내는 부호 연산자가 되기도 한다.</p>
</li>
</ul>
<pre><code class="language-java">-3 - 5;</code></pre>
<br/>

<h3 id="연산자-우선순위와-결합규칙">연산자 우선순위와 결합규칙</h3>
<ul>
<li>연산자 우선 순위는 헷갈리지 않게 알아두는 것이 좋다. 
이것도 작업의 순서를 결정하는 것이기 때문에 잘못 알고 있다면 결과 자체가 달라질 수 있기 때문이다.
다행인 건 대부분 기본 상식 안에서 해결되기 때문에 강박적으로 외워둘 필요는 없다.</li>
</ul>
<p><strong>연산자 우선순위</strong></p>
<ul>
<li>사용 방식 : 산술 &gt; 비교 &gt; 논리 &gt; 대입</li>
<li>피연산자의 수 : 단항 &gt; 이항 &gt; 삼항</li>
</ul>
<blockquote>
<p>다음은 예시와 주의해서 볼 부분을 표로 나타낸 것이다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/ho_c/post/76a300c1-f47d-4edb-a494-b409b7dde0ce/image.jpg" alt=""></p>
<p><strong>연산자 결합규칙</strong></p>
<blockquote>
<p>그렇다면 한 식 안에 같은 우선순위를 지닌 연산자가 있다면 어떻게 해야될까?
→ 결합규칙을 사용한다.</p>
</blockquote>
<ul>
<li>결합규칙은 왼쪽에서 오른쪽의 순서로 연산을 수행한다. 대부분 이렇다.
단, 단항 연산자와 대입만 그 반대<strong>(오른쪽 → 왼쪽)</strong>로 수행한다.</li>
</ul>
<pre><code class="language-java">y = 3 + 4 – 5;

// 왼 → 오
y = 7 – 5;
y = 2;

// 오 → 왼
y = 2; // y라는 변수 안에 리터럴 2가 저장됨.</code></pre>
<br/>

<h3 id="산술-변환">산술 변환</h3>
<blockquote>
<p>산술변환은 이전 파트의 끝에서 설명한 ‘형변환’이다. 먼저 이항 연산자는 두 피연산자의 타입이 일치해야 연산이 가능하다. 이는 저장방식의 차이가 존재하기에 당연한 얘기이다. </p>
</blockquote>
<ul>
<li>그래서 컴파일러는 큰 타입과 작은 타입 간의 연산의 형변환을 자동으로 진행해 생략이 가능한데, 이를 산술 변환이라고 한다.
_**다만 이전에 설명하지 않았던 두 규칙이 존재한다.</li>
<li>*_<h4 id="①-서로-다른-피연산자의-타입은-보다-큰-타입으로-일치된다">① 서로 다른 피연산자의 타입은 보다 큰 타입으로 일치된다.</h4>
</li>
</ul>
<p>&emsp; long + int → long + long → long</p>
<p>&emsp; 이는 작은 타입으로 큰 타입을 넣으면 값 손실이 일어나기 때문이다.</p>
<h4 id="②-피연산자들의-타입이-int보다-작으면-int로-변환된다">② 피연산자들의 타입이 int보다 작으면 int로 변환된다.</h4>
<p>&emsp; byte + short → int + int → int</p>
<p>&emsp; 작은 타입은 그 결과값이 넘쳐 오버플로우가 일어날 확률이 높다. 그래서 int로 변환된다.</p>
<p>*<em>추가로 연산자의 결과 역시, 피연산자의 타입을 따른다는 것을 잊지 말자. *</em></p>
<pre><code class="language-java">5/2 == 2; // int /  int ==  int
5/2.0f == 2.5; // int / float == float</code></pre>
<br/>

<p><strong>￮ 요약</strong></p>
<ul>
<li>연산자는 결과를 반환한다.</li>
<li>산술 &gt; 비교 &gt; 논리 &gt; 대입 </li>
<li>대입은 제일 마지막에 수행된다.</li>
<li>단항(1) &gt; 이항(2) &gt; 삼항 (3). 단항 연산자의 우선순위가 이항 연산자보다 높다.</li>
<li>단항 연산자와 대입 연산자를 제외한 모든 연산의 진행 방향은 왼쪽에서 오른쪽이다.</li>
<li>산술 변환은 작은 타입은 큰 타입으로 자동 형변환된다.</li>
<li>다만 int보다 작을 경우 자동으로 int로 변환해서 연산된다.</li>
<li>연산자의 결과는 피연산자의 타입을 따른다.</li>
</ul>
<hr>
<h2 id="2-단항-연산자">2. 단항 연산자</h2>
<ul>
<li>단항 연산자는 우선순위가 가장 높으며, 결합규칙이 오른쪽에서 왼쪽으로 진행된다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/ho_c/post/893cc13c-efd3-46f9-a3d0-664aaa8446c0/image.jpg" alt=""></p>
<h4 id="증감-연산자-----">증감 연산자 : ++ --</h4>
<ul>
<li><p>증감 연산자는 피연산자에 저장된 값을 1씩 증가 or 감소시킨다. 
정수, 실수 모두 가능하지만 상수(constant)인 경우는 불가능하다.</p>
</li>
<li><p>보통 연산자는 평가만 할 뿐 피연산자 그 자체를 변경하지 않는다. 
오직 증감 연산자와 대입 연산자만 피연산자의 값 자체를 변경시킨다.</p>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/ho_c/post/39bbed82-ff33-4aa5-8356-38046ffed952/image.jpg" alt=""></p>
<p>증감연산자에서 중요한 것은 전위형인지, 후위형인지이다. 많이들 헷갈려하는 부분인데, 일단 증감 연산자가 홀로 쓰일 경우는 그닥 신경쓰지 않아도 된다.</p>
<pre><code class="language-java">i++;
++i;</code></pre>
<p>위처럼 단독을 쓰일 경우, 변수에 접근해서 값을 불러오는 ‘참조’가 홀로 되기 때문에 딱히 문제가 되지 않는다. 하지만 밑에 코드 일 경우 값이 달라진다.</p>
<pre><code class="language-java">int i=5, j=0;

j = i++; 
System.out.println(&quot;j=i++; 실행 후, &quot; + i +&quot;, j=&quot;+j);

i=5; // 초기화
j=0;

j = ++i;
System.out.println(&quot;j++; 실행 후, i=&quot; + +&quot;, j=&quot;+j);</code></pre>
<p>결과는 첫 번째는 <code>i=6, j=5</code>, 두 번째는 <code>i=6, j=6</code>이다. 기본적으로 전위든 후위든 1씩 증가하는 건 맞지만 값이 증가하는 타이밍이 다르다.</p>
<ul>
<li>전위형 : 메모리에 저장된 값을 꺼내기 전에 증가.</li>
<li>후위형 : 메모리에 저장된 값을 꺼내서 연산 후, 증가.</li>
</ul>
<br/>

<h3 id="부호-연산자----">부호 연산자 : + -</h3>
<ul>
<li>산술연산에도 쓰이는 <code>+ -</code>는 단항으로 쓰면 부호연산자이다. 말 그대로 부호를 바꿔준다. 
다만 수학공식처럼 음수에 음수부호(-)면 양수, 양수에 음수부호(-)면 음수를 반환한다.</li>
</ul>
<pre><code class="language-java">int i = -10;
i = +i; // -10

i = - 10;
i = -i; // 10</code></pre>
<br/>

<p><strong>￮ 요약</strong></p>
<ul>
<li>단항에는 증감과 부호연산자가 있다.</li>
<li>증감은 전위형, 후위형이 있으며 값을 참조하는 타이밍이 다르다.</li>
<li>증감을 한 식 안에 여러번 쓰는 것 좋지 않다.</li>
<li>부호 연산자는 boolean, char를 제외한 기본형에만 사용 가능하다.</li>
</ul>
<hr>
<h2 id="3-산술-연산자">3. 산술 연산자</h2>
<ul>
<li>산술 연산자는 사칙연산, 나머지 연산자가 있다. 말 그대로 산술 용도로 쓰는 연산자이다.
당연한 얘기니까 주의사항 중심으로 전개하자.</li>
</ul>
<h3 id="사칙-연산자------">사칙 연산자 : + - * /</h3>
<ul>
<li><p>*<em>우선순위는 곱셈, 나눗셈이 높고 그다음으로 덧셈, 뺄셈이다. *</em></p>
<br/>
</li>
<li><p><strong>정수형 나눗셈의 피연산자로 0을 집어넣으면 에러가 발생한다. 컴파일은 된다.</strong></p>
</li>
</ul>
<pre><code class="language-java">System.out.println(3/0); // 실행하면, 오류 (ArithmeticException)
System.out.println(3/0.0); // 지수부가 -127이라, 0출력</code></pre>
<br/>

<ul>
<li><strong>나누기 연산 시, int타입인 경우 연산 결과도 int라서 소수점 이하는 버려진다. 이때 반올림은 발생하지 않는다.</strong></li>
</ul>
<pre><code class="language-java">10 / 4; // → 2</code></pre>
<p>소수점 아래값을 얻고 싶다면 float, double과 같이 연산을 해야된다. 반올림을 하고 싶다면 <code>Math.round()</code> 메서드를 사용하거나 $+0.5$로 원하는 자리에서 반올림이 되게 해야한다.</p>
<pre><code class="language-java">double pi- 3.141592;
double shortPi - (int) (pi * 1000 + 0.5) / 1000.0;
// double shortPi = Math.round(pi * 1000) / 1000.0
// Math.round() 메서드는 파라미터의 소수점 첫 번째 자리에서 반올림한다.</code></pre>
<br/>

<ul>
<li><strong>int보다 작은 값은 int형으로 변환되서 int형으로 결과를 반환한다.</strong><pre><code class="language-java">byte a = 10:
byte b = 20;
byte c = a + b // 에러발생. 명시적 형변환을 해줘야됨.
</code></pre>
</li>
</ul>
<p>byte d = 100:
byte e = 30;
byte f = (byte) d + e //저장은 되지만, 오버플로우가 발생한다.</p>
<pre><code>&lt;br/&gt;

- **오버플로우가 발생한 값은 형변환을 해도, 정상값이 되진 않는다.**
```java
int a = 1_000,000; // 1,000,000
int b = 2_000_000; // 2,000,000
long c = a * b;

System.out.println(c); // -1454759936 : 오버플로우된 값이 저장됨.</code></pre><p>그래서 값이 넘을 것 같으면, 애초에 큰 타입으로 연산을 수행하는 것이 좋다.</p>
<br/>

<ul>
<li><strong>사칙연산의 피연산자로 문자도 가능하다.</strong>
문자는 실제 유니코드에 따라 연속적으로 배치된 부호 없는 정수이다. 그래서 정수 간 연산이 가능하며, 동시에 연속적 배치를 활용해서 다른 값으로 변환할 수 있다.</li>
</ul>
<p><strong>① 문자 → 숫자 변환법</strong></p>
<pre><code class="language-java">‘2’ - ‘0’ → 50 – 48 → 2</code></pre>
<p>따라서 문자형을 정수형으로 변환하면 되는데, 이는 유니코드의 숫자가 연속적으로 배치되어 있기에 가능하다.</p>
<p><strong>② 대문자 ⟷ 소문자 변환법</strong></p>
<pre><code class="language-java">char lowerCase = &#39;a&#39;;
char upperCase (char) (lowerCase - 32);</code></pre>
<p>이 역시도 유니코드의 연속적 배치를 활용한 것이다. <code>’a’</code>는 97, <code>‘A’</code>는 65로 두 코드의 차이는 32이다. 그래서 소문자에서 –32를 한 다음, char에 저장하면 ‘A’가 저장된다.</p>
<p>반대로 대문자에 +32를 하면 소문자로 변환되서 저장된다. 이를 활용해서, 대소문자를 구별할 수 있다.</p>
<br/>

<ul>
<li>** 상수, 리터럴 간의 연산은 컴파일 과정에서 컴파일러가 미리 계산한다.**<pre><code class="language-java">  char cl = &#39;a&#39;;
//     char c2 = c1+1; // 컴파일 에러
  char c2 = &#39;a&#39;+1;</code></pre>
</li>
</ul>
<p>위 코드에서 두 번째 라인은 컴파일 에러가 발생한다. 왜냐면, 변수는 컴파일러가 미리 연산을 할 수 없기 때문이다. 반대로 리터럴 간에는 이미 값이 정해져 있기 때문에 컴파일러가 컴파일 과정에서 미리 처리한다.</p>
<br/>

<h3 id="나머지-연산자--">나머지 연산자 : %</h3>
<ul>
<li><p>나머지 연산자는 왼쪽의 값을 오른쪽으로 나누고 난 나머지 값을 결과로 반환한다.
이 역시, 나누는 수로 0은 불가능하다.</p>
</li>
<li><p><strong>나머지 연산을 이용해서 짝수, 홀수, 배수 검사로 활용할 수 있다.</strong></p>
<pre><code class="language-java">int x = 9;
int y = 3;
z = x % y; // z = 0;
// 3으로 나눴을 때, 나머지가 0이므로 9는 3의 배수이다.
</code></pre>
</li>
</ul>
<pre><code>- 나누는 수로 음수도 가능하지만 부호는 무시된다. 다만 왼쪽 피연산자의 부호는 인정된다.


------

## 4. 비교 연산자
- 말 그대로 피연산자 간의 값을 비교하는데 사용되는 연산자이다. 다만 연산결과가 `true, false`이다.
- 이 역시 이항 연산자이기에 비교하는 연산자 간 타입이 다를 경우 큰 타입으로 산술변환해서 비교한다.

&lt;br/&gt;

### 대소비교 연산자 : &lt; &gt; &lt;= &gt;=

- 대소비교 연산자의 기준은 좌변의 값이다.
- boolean형을 제외한 기본형에만 사용이 가능하다.

&lt;br/&gt;

### 등가비교 연산자 : == !=

- 두 연산자의 값이 같은지, 다른지 비교한다.
- 모든 자료형(기본, 참조)에 사용이 가능하다. 다만 기본형일 때는 ‘값’, 참조형일 때는 ‘주소’를 비교한다는 것만 유의하자.

- 실수형에 경우에는 저장과정에서 정밀도 차이가 발생해서, 같은 십진수라도 다를 수가 있다.

```java
float f = 0.1f;
double d = 0.1;
double d2 = (double) f; // 이미 이진수로 변환된 걸, 큰 자료형에 넣는다고 해서 정상값이 되지 않는다.

System.out.println(f == d); //  false
System.out.println(d == d2); // false
System.out.println(f == d2); // true</code></pre><br/>

<h3 id="문자열-비교">문자열 비교</h3>
<ul>
<li><p>문자열 비교를 위해선 <code>.equals()</code>라는 메서드를 사용한다.</p>
</li>
<li><p>앞서 보았듯이 <code>==</code>은 기본형에서는 값, 참조형에선 주소를 비교한다. 
주소 역시 16진법의 형태로 저장되기 때문에 비교는 가능하다.
하지만 비교 대상이 다르기에 같은 문자열이라도 <code>false</code> 가 반환될 수 있다.</p>
</li>
</ul>
<pre><code class="language-java">String str1 = new String(&quot;abc&quot;);
String str2 = &quot;abc&quot;;
System.out.println(str1 == str 2);  // false
System.out.println(str1.equals(str2)); // true</code></pre>
<p>일단 문자열은 같지만 서로 다른 객체의 주소를 변수에 저장하고 있다. 여기서 str1에는 JVM의 heap 메모리에 생성된 객체 주소가, str2는 공유풀에 있는 객체 주소가 저장된다.</p>
<p>따라서 <code>equals()</code>를 사용하면 문자열끼리만 비교가 가능하다. 
(대소문자 무시 : <code>equalslignoreCase()</code>사용)</p>
<hr>
<h2 id="5-논리-연산자">5. 논리 연산자</h2>
<blockquote>
<p>비교 연산자는 이항 연산자이다. 따라서 피연산자 두 개로 하나의 조건을 만든다. 
<em><strong>그럼 조건이 두 개 이상일 때는 어떻게 해야할까?</strong></em>
→ 그럴 때 사용하는 것이 논리연산자이다.</p>
</blockquote>
<h3 id="논리-연산자----">논리 연산자 : &amp;&amp; || !</h3>
<ul>
<li>&amp;&amp; : AND연산으로 피연산자 양쪽 모두 true여야만 true를 얻는다.</li>
<li>|| : OR연산으로 피연산자 중 한쪽만 true여도 true를 얻는다.</li>
<li>! : 부정연산자로 연산결과를 역전시킨다.</li>
</ul>
<blockquote>
<p>예시와 함께 보도록하자.</p>
</blockquote>
<p><strong>① x는 1보다 크고 10보다 작다.</strong><br>&emsp;&emsp; x &gt; 1
&emsp;&emsp; x &lt; 10
&emsp;&emsp; ⇒ <strong>1 &lt; x &amp;&amp; x &lt; 10</strong></p>
<p><strong>② x는 3의 배수 또는 5의 배수이다.</strong>
    &emsp;&emsp; x%3 == 0
    &emsp;&emsp; x%5 == 0
    &emsp;&emsp; ⇒ <strong>x%3 == 0 || x%5 == 0</strong></p>
<p><strong>③ x는 2의 배수 또는 3의 배수지만 6의 배수는 아니다.</strong>
    &emsp;&emsp; x%2==0
    &emsp;&emsp; x%3==0
    &emsp;&emsp; x%6!=0
    &emsp;&emsp; ⇒ <strong>( x%2==0 || x%3==0 ) &amp;&amp;  x%6!=0</strong></p>
<blockquote>
<p>위 경우 앞서 말했듯이 &amp;&amp;의 연산순위가 ||보다 높기 때문에 ()로 묶어줘서 연산순위를 높여줘야 한다.</p>
</blockquote>
<p><strong>④ 문자 ch는 소문자’a’-‘z’이다.</strong>
    &emsp;&emsp;     97 &lt;= ch
    &emsp;&emsp;     ch &lt;= 122
    &emsp;&emsp;     <strong>97 &lt;= ch &amp;&amp; ch &lt;= 122</strong></p>
<p>유니코드의 연속적 배치 때문에 가능한 논리식이다. 97은 소문자 a, 122는 소문자 z이다. 같은 방식으로 대문자, 숫자인지 확인하는 논리식을 표현할 수 있다.</p>
<hr>
<h4 id="효율적인-연산">효율적인 연산</h4>
<blockquote>
<p>논리연산자를 사용한 식에 대해서 컴퓨터는 효율적으로 연산을 한다.</p>
</blockquote>
<ul>
<li>|| (OR) : 좌측 피연산자가 <code>true</code> 이면 우측 피연산자 평가 X</li>
<li>&amp;&amp; (AND) : 좌측 피연산자가 <code>false</code> 이면 우측 피연산자 평가 X</li>
</ul>
<blockquote>
<p>이는 OR의 경우 한쪽만 참이어도, true를 반환하고, AND의 경우는 한쪽만 거짓이어도 false를 반환하기 때문이다. 
그래서 연산속도를 높이려면, 각 조건에 맞게 효율적인 연산이 되도록 확률이 높은 조건을 좌측에 놓는 것이 좋다.</p>
</blockquote>
<hr>
<br/>

<h3 id="논리-부정-연산자--">논리 부정 연산자 : !</h3>
<ul>
<li><code>!</code> 부정연산자를 사용하면, 결과가 반대로 바뀌어서 반환되며 ‘~이 아니다’로 이해할 수 있다.
!true → false
!false → true</li>
</ul>
<br/>

<p><strong>￮ 요약</strong></p>
<ul>
<li>연산자에 따라 좌측 피연산자를 중심으로 효율적인 연산을 한다.</li>
<li>부정 연산자는 논리값을 반전 시킨다.</li>
</ul>
<br/>

<h3 id="비트-연산자----">비트 연산자 : | &amp; ^</h3>
<ul>
<li><p>비트 연산자는 말 그대로 피연산자를 비트 단위로 논리 연산한다. 
주로 전가산기를 비트 연산자를 사용해서 구현하곤 한다. 
이때, 비트 연산자의 피연산자는 정수(문자 포함)만 허용된다.</p>
<p>  | (OR) : 피연산자 중 한 쪽 값이 1이면 1을 얻는다.
  &amp; (AND)  : 양쪽 피연산자가 1일 때만 1을 얻는다. 
  ^ (XOR) : 피연산자가 서로 다를 때만 1을 얻는다. 같으면 0</p>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/ho_c/post/6b82882d-2302-4f39-9f3f-c5c8db2ebadc/image.jpg" alt=""></p>
<blockquote>
<p>위 표를 보고 대충 어떻게 연산이 되는지는 알 수 있었다. 그럼 실제 사용을 어떤 식으로 할까?</p>
</blockquote>
<ul>
<li>| : 특정 비트의 값을 변경할 때 사용.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/ho_c/post/aefb3680-e47d-4b37-bb8d-bfc16aaec318/image.jpg" alt=""></p>
<ul>
<li>&amp; : 특정 비트의 값을 추출할 때 사용.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/ho_c/post/1724bff5-3519-4521-b072-05e95b3559ff/image.jpg" alt=""></p>
<ul>
<li>^ : 단순 암호화</li>
</ul>
<p><img src="https://velog.velcdn.com/images/ho_c/post/b6bc70a4-cbd5-4589-8aec-73c35a23dba2/image.jpg" alt=""></p>
<br/>


<h3 id="비트-전환연산자--">비트 전환연산자 : ~</h3>
<ul>
<li>논리 부정연산자처럼 비트를 반전한다. 
이를 응용하여 2진수에 대한 1의 보수를 얻어낼 수 이다. 
<code>x == 1</code> → <code>~x == 0</code></li>
</ul>
<p><img src="https://velog.velcdn.com/images/ho_c/post/44917d7f-23a5-4e86-b223-0215a6a80cc4/image.jpg" alt=""></p>
<br/>

<h3 id="쉬프트-연산자-">쉬프트 연산자 : &lt;&lt; &gt;&gt;</h3>
<ul>
<li>피연산자를 2진수를 표현했을 때, 오른쪽(&gt;&gt;), 왼쪽(&lt;&lt;)으로 이동시킨다.</li>
</ul>
<blockquote>
<p><strong>쉬프트 연산자는 다음과 같이 동작한다.</strong></p>
</blockquote>
<p>&emsp;&emsp; <strong>① 10진수 6을 2진수로 바꾼다. (00000110)</strong></p>
<p><img src="https://velog.velcdn.com/images/ho_c/post/ef72f945-64b8-48c6-b0f7-694c0ba4def5/image.jpg" alt=""></p>
<p>&emsp;&emsp; <strong>② <code>6&lt;&lt;2</code> 는 왼쪽으로 2칸 이동시킨다.</strong></p>
<p><img src="https://velog.velcdn.com/images/ho_c/post/53cb3bcc-940b-4930-a40e-ad034ad4315c/image.jpg" alt=""></p>
<p>&emsp;&emsp; <strong>③ 범위를 넘어간 값은 버려지고(초록), 빈자리(노랑)는 0으로 채워진다.</strong></p>
<p>&emsp;&emsp; <strong>④ 이를 다시 10진수로 바꾸면 24가 된다.</strong></p>
<blockquote>
<p>이런 점에서 쉬프트 연산자는 $2^n$의 곱셈과 나눗셈으로 정리된다.</p>
</blockquote>
<ul>
<li><code>x &lt;&lt; n</code> : $x$ x $2^n$</li>
<li><code>x &gt;&gt; n</code> : $x$ / $2^n$</li>
</ul>
<p>물론 십진수의 곱셈, 나눗셈과 큰 차이는 없지만 비트만 이동해서 연산이 되기 때문에 빠른 연산 속도를 요할 때 주로 사용한다.</p>
<br/>

<p><strong>￮ 요약</strong></p>
<ul>
<li>자리올림 때문에 OR보단 XOR이 낫다.</li>
<li>비트연산에서도 피연산자의 타입 일치를 위한 ‘산술변환’이 일어날 수 있다.</li>
<li>비트연산 결과를 2진수로 출력하고 싷다면 <code>toBinaryString()</code> 메서드를 사용하자.</li>
<li>비트 전환 연산자는 피연산자의 타입이 int보다 작으면 int로 산술변환 후 연산한다.</li>
<li><code>&gt;&gt;</code> 는 좌측 피연산자가 음수인 경우, 빈자리를 1로 채운다. (부호)</li>
<li>쉬프트 연산자는 n의 값이 자료형의 bit수보다 크면 $n$ % $bit의 수$로 계산한다.</li>
</ul>
<hr>
<h2 id="6-그-외의-연산자">6. 그 외의 연산자</h2>
<h3 id="조건-연산자--">조건 연산자 ? :</h3>
<ul>
<li>조건 연산자는 &lt;조건식, 식1, 식2&gt; 3개의 피연산자를 갖는 삼항 연산자이다.</li>
</ul>
<blockquote>
<p>조건식 ? 식1 : 식2</p>
</blockquote>
<ul>
<li>조건식의 값이 true면 <strong>(식1)</strong>을 false면 <strong>(식2)</strong>를 반환한다.</li>
</ul>
<pre><code class="language-java">result = (x &gt; y) ? x : y; // true → result = x;
result = (x &gt; y) ? x : y; // false → result = y;</code></pre>
<ul>
<li>조건 연산자는 중첩해서 사용이 가능하다. <pre><code class="language-java">x = -1;
result = (x &gt; 0) ? 1 : ( x == 0 ? 0 : -1);
</code></pre>
</li>
</ul>
<p>// 풀이
result = (x &gt; 0) ? 1 : ( -1 == 0 ? 0 : -1);
result = (x &gt; 0) ? 1 : ( false ? 0 : -1);
result = (x &gt; 0) ? 1 : -1;
result = false ? 1 : -1;
result = -1;</p>
<pre><code>
&gt; 이처럼 연산자의 결합규칙에 따라서 오른쪽부터 왼쪽으로 실행이 된다. 
다만 조건 연산자를 여러 번 중첩해서 사용하면 가독성이 떨어지기 때문에, 조건문 if로 풀어서 쓰는 게 낫다.

&lt;br/&gt;

### 대입 연산자 : =

- 대입 연산자는 아마도 가장 많이 사용하게 될 연산자로 기능은 저장공간에 리터럴, 연산 결과를 저장하고, **그 값을 연산 결과로 반환한다**.

- 결합 규칙에 따라서 왼쪽에서 오른쪽으로 연산되며, **좌측의 피연산자는 값을 저장할 수 있는 저장공간이어야 한다.
**

#### 복합 대입 연산자

다른 연산자가 포함된 대입 연산자의 식을 축약한 형태이다. 딱히 대단한 건 아니지만 잘 모르겠다면 그냥 풀어서 쓰는게 낫다.

```java
a += 3; //  a = a + 3;
a -= 3; //  a = a - 3;
a *= 3; //  a = a * 3;
a &lt;&lt;= 3; //  a = a &lt;&lt; 3; // a * $2^3$
a &amp;= 3; //  a = a &amp; 3;
a ^= 3; //  a = a ^ 3;
a |= 3; //  a = a | 3;
a *= 7+b //  a = a * (7+b);</code></pre><br/>

<p><strong>￮ 요약</strong></p>
<ul>
<li>삼항 연산자도 이항 연산자처럼 산술변환을 한다.</li>
</ul>
<br/>

<hr>
<blockquote>
<p>도움이 되셨다면 <strong>&#39;좋아요&#39;</strong> 부탁드립니다 :)</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[Ch02. 변수]]></title>
            <link>https://velog.io/@ho_c/%EA%B8%B0%EC%B4%88-%EC%9E%90%EB%B0%94%EC%9D%98-%EC%A0%95%EC%84%9D-3%ED%8C%90-%EB%B3%80%EC%88%98</link>
            <guid>https://velog.io/@ho_c/%EA%B8%B0%EC%B4%88-%EC%9E%90%EB%B0%94%EC%9D%98-%EC%A0%95%EC%84%9D-3%ED%8C%90-%EB%B3%80%EC%88%98</guid>
            <pubDate>Thu, 23 Mar 2023 11:36:42 GMT</pubDate>
            <description><![CDATA[<h1 id="들어가는-말">들어가는 말</h1>
<ul>
<li>프로그래밍 언어의 기본은 자료, 곧 Data를 다루는 일이다.
물론 반복문, 제어문으로 흐름 제어도 있지만, 자료가 없으면 컴퓨터가 딱히 할 일은 없을 것이다. 그래서 컴퓨터에게 일을 시키기 위한 첫 번째 단계는 값을 던져주는 일이고, 컴퓨터에게 값을 던져주는 공간을 “변수”라고 한다.</li>
</ul>
<hr>
<h2 id="1-변수와-상수">1. 변수와 상수</h2>
<br/>

<p> <strong>변수란?</strong></p>
<ul>
<li><p>단 하나의 값을 저장할 수 있는 메모리 상의 공간.</p>
</li>
<li><p>쉽게 말해 변수란 아파트 우편함과 유사하다. 또는 헬스장 락커처럼 물건을 넣을 수 있는 공간이라고 이해하는게 편하다. 그리고 이 공간은 운영체제에 의해 관리된다.</p>
</li>
</ul>
<p><strong>￮ 요약</strong></p>
<ul>
<li><p>메모리상의 공간</p>
</li>
<li><p>하나의 변수에 하나의 값만 저장 가능</p>
</li>
<li><p>변수의 선언과 초기화</p>
</li>
<li><p>변수를 사용하기 위해선 1) 선언 2) 초기화의 과정을 거친다.</p>
</li>
</ul>
<br/>

<h3 id="변수-선언">변수 선언</h3>
<ul>
<li>공간을 사용하기 위해선 분류가 필요하듯이 변수를 사용하기 위해선 해당 변수의 용도와 이름을 컴퓨터에게 알려줘야 한다.</li>
</ul>
<pre><code class="language-java">int age;</code></pre>
<blockquote>
<p>int는 변수타입, age는 변수명인데 실제 변수명은 메모리 상에서 “주소”의 형태로 관리된다. 
사람이 이해하기 편하게 변수명으로 대체되는 것이다.
*<em>이제 우리는 변수명이란 주소를 이용해 메모리에 접근해 변수를 다룰 수 있게 된다.
*</em></p>
</blockquote>
<br/>

<h3 id="초기화">초기화</h3>
<ul>
<li><p>초기화는 선언된 변수에 처음으로 값을 넣어주는 것이다.
단순한 초기화는 변수 선언과 함께 진행해 따로 절차를 구분하진 않는다. 
그래도 초기화를 왜 해야되는지는 알아야 한다.</p>
</li>
<li><p>메모리는 CPU의 작업 대상을 미리 올려놓는 휘발성 저장공간이다. 
그래서 단순 작업 중에도 보이지 않는 프로그램들이 메모리 상에 올라가 운용되고, 이를 운영체제에 의해 스케쥴링 되고 있다.</p>
</li>
</ul>
<blockquote>
<p>그래서 우리가 선언한 변수 안에 미쳐 치우지 못한 데이터들이 있을 수도 있어서, 대입연산자(=)를 통해 해당 값을 변수 안에 저장시켜 초기화 한다.</p>
</blockquote>
<pre><code class="language-java">int age;
age = 25;</code></pre>
<br/>

<p><strong>￮ 요약</strong></p>
<ul>
<li>변수를 쓸려면 컴퓨터한테 알려줘서 메모리를 특정해 한다.</li>
<li>메모리는 공간을 주소 형태로 운용, 즉 변수는 실제 형태는 ‘주소’이다.</li>
<li>이걸 인간이 직접 하나씩 외우기엔 불편하다.</li>
<li>고로 변수명을 사용, 또한 변수타입으로 공간의 용도를 특정해줘야 한다.</li>
<li>초기화를 안하면 이전 값이 남아있을 때도 있다.</li>
</ul>
<br/>

<h3 id="두-변수의-값-교환하기">두 변수의 값 교환하기</h3>
<pre><code class="language-java">int x = 20;
int y = 10;</code></pre>
<p>두 변수가 있다. 이 둘의 값을 바꿔보자.</p>
<pre><code>방법은 간단하다.
만약 양손에 사과가 있다고 치자, 이 둘의 위치를 바꾸려면 어떻게 해야될까?
저글링을 하면 쉽겠지만, 컴퓨터는 저글링을 못한다.

단, 한번에 바꿀려 하지말고 한 손의 사과를 바닥에 내려놓고 옮긴 다음 내려놓은 사과를 남은 손으로 잡으면 된다.</code></pre><p>위 방식대로 하면 그냥 변수 하나 더 선언하면 된다.</p>
<pre><code class="language-java">int x = 20; // 왼손
int y = 10; // 오른손
int tmp; // 바닥

tmp = x; // 바닥에 내려놓고
x = y; // 오른손에 있는 걸 왼손으로 옮기고
y = tmp; // 바닥에 있는 걸 오른손으로 집어든다.</code></pre>
<br/>

<h3 id="변수의-명명규칙">변수의 명명규칙</h3>
<ul>
<li>맘대로 지을 수 있다면 좋겠지만, 그러면 혼란이 온다. 
또 프로그래밍 언어마다 미리 특정된 언어(예약어)들이 존재하기 때문에 이는 피해야 한다. 
따라서 구분을 위한(식별) 규칙이 있다.</li>
</ul>
<p>&emsp;① 대소문자 구분, 길이 제한 X
&emsp;&emsp;② 예약어는 변수명으로 사용하면 안된다.
&emsp;&emsp;③ 숫자로 시작하면 안된다.
&emsp;&emsp;④ 특수문자로 ‘_’와 ‘$’만 허용된다.</p>
<blockquote>
<p>예약어는 쓰다 보면 외워진다. 미리 외워봤자 그 용도를 모르기 때문에 딱히 달달 외워둘 필요는 없다.</p>
</blockquote>
<p>추가로 프로그래머 간의 convention은 중요하다. 일종의 예의이니 미리 숙지해두는게 좋다.</p>
<ul>
<li>클래스 명의 첫 글자는 대문자</li>
<li>여러 단어로 이루어진 이름은 카멜 케이스로 구분 (IndexOf)</li>
<li>상수는 모두 대문자, 이때 여러 단어는 스네이크 케이스로 구분 (MAX_NUMBER)</li>
</ul>
<hr>
<h2 id="2-변수의-타입">2. 변수의 타입</h2>
<p>-
컴퓨터로 다루는 값은 모두 ‘숫자’지만, 인간의 관점에선 ‘문자와 숫자’ / 숫자는 <strong>‘정수, 실수’</strong>로 구분된다.</p>
<ul>
<li>그리고 이것들이 다 표현하는 범위가 다르고, 메모리를 다루는 방식이 다르기에 값의 종류를 미리 나누어 놨는데,  이를 <strong>“자료형”</strong>이라고 한다.</li>
</ul>
<br/>

<h3 id="기본형과-참조형">기본형과 참조형</h3>
<ul>
<li><p>자료형은 기본형과 참조형으로 나뉜다.
이 둘은 메모리 상에서 다루는 위치가 구분된다. 자세한 건 나중에 보도록 하자.</p>
</li>
<li><p>무튼 기본형에는 실제 값 자체가 들어가고, 참조형에는 실제 값이 저장되어 있는 <strong>“주소”</strong>를 값으로 가진다.</p>
</li>
</ul>
<blockquote>
<p>굳이 예를 들면 지하철 물건 보관함에 기본형은 돈가방이, 참조형엔 돈가방의 위치가 들어있다고 생각하면 된다.</p>
</blockquote>
<p><strong>기본형 예</strong></p>
<pre><code class="language-java">int a = 10;</code></pre>
<p><strong>참조형 예</strong></p>
<pre><code class="language-java">Date today = new date();</code></pre>
<ul>
<li><p>참조변수는 클래스 타입을 변수 타입으로 가져가며, 앞서 말한 것처럼 객체의 주소를 저장한다. - 여기서 new연산자는 클래스를 인스턴스화 하는 예약어이다. 
이제 today이라는 참조변수명을 통해서 Date 인스턴스를 사용할 수 있다.</p>
</li>
<li><p>이때 참조변수의 주소는 null or 0x0~0xFFFFFFFF로 저장된다. (JVM 32bit) 
16진수 한자리 = 4bit </p>
</li>
</ul>
<br/>

<p><strong>￮ 요약</strong></p>
<ul>
<li>기본형 : boolean(논리), char(문자형, 사실상 정수), { byte, short, int, long }(정수), { float, double }(실수) </li>
<li>참조형 : 객체 주소 </li>
<li>참조변수는 클래스 타입을 변수 타입으로 가져감</li>
<li>C언어와 달리 참조형 변수와는 연산 불가다.</li>
</ul>
<br/>


<h3 id="기본형">기본형</h3>
<ul>
<li>기본형은 8개(boolean, char, byte, short, int, long, float, double)이 있으며,
논리, 정수, 실수으로 구분한다. 
(char도 실방식은 정수, 그래서 정수형과 연산이 가능하다.)</li>
</ul>
<blockquote>
<p>이러한 구분은 저장방식과 저장할 값의 범위의 영향을 받았다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/ho_c/post/ba2a552d-9669-4ddf-8330-b94542b42bd8/image.JPG" alt=""></p>
<p><img src="https://velog.velcdn.com/images/ho_c/post/3db18459-5875-440c-a978-dd7d62a00bc1/image.JPG" alt=""></p>
<p><strong>￮ 요약</strong></p>
<ul>
<li>기본형(8개)은 저장방식, 크기, 범위에 따라 논리, 정수, 실수로 구분된다.</li>
<li>정수의 범위($2^{n-1}$~$2^{n-1}$$-1$) 대충 int는 10자리, long은 19자리이다.</li>
<li>실수는 정밀도가 중요하다. float(7) : 소수점 6자리까지 / double(15) : 소수점 14자리까지  </li>
</ul>
<br/>

<h3 id="상수와-리터럴">상수와 리터럴</h3>
<h4 id="1-상수constant">1) 상수(constant)</h4>
<ul>
<li>상수(constant) 역시 값을 저장할 수 있는 메모리 공간이다. 
다만 한번 저장하면 다른 값으로 변경할 수 없다.</li>
</ul>
<blockquote>
<p>선언 방법은 변수와 동일하지만, 상수 앞에는 <code>final</code>이라는 예약어가 붙는다.</p>
</blockquote>
<pre><code class="language-java">final int MAX_Value = 10; // int타입 상수 선언과 초기화</code></pre>
<p>￮ 요약</p>
<ul>
<li>상수는 선언과 초기화가 동시에 해야만 한다.</li>
<li>대문자 표기가 관습이며, 스네이크 케이스(_)로 단어를 구분한다.</li>
</ul>
<br/>

<h4 id="2-리터럴literal">2) 리터럴(literal)</h4>
<ul>
<li>실제 저장되는 값들 역시 상수이다. 
다만 기존 상수의 정의와 구별하기 위해 값은 ‘리터럴’이라고 칭한다.</li>
</ul>
<br/>

<h4 id="3-상수의-필요성">3) 상수의 필요성</h4>
<ul>
<li>상수는 코드의 유지보수성을 높이기 위해서 사용된다.</li>
</ul>
<pre><code class="language-java">final int WIDTH = 20;
final int HEIGHT = 10;

int triangleArea = (WIDTH * HEIGHT) / 2;</code></pre>
<p>위 코드에서 삼각형의 면적을 변경하고 싶으면 상수만 변경하면 된다. 물론 변수여도 가능하지만 상수는 중간에 값을 변경할 수 없기 때문에 코드의 안정성 면에서 적합하다.</p>
<br/>

<h4 id="4-리터럴의-타입과-접미사">4) 리터럴의 타입과 접미사</h4>
<ul>
<li>리터럴 역시 타입이 존재한다. 변수 타입이 있으니, 그에 맞는 값의 타입 역시 존재하는게 맞다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/ho_c/post/7cad8362-cee2-433f-ad49-6d51a404a919/image.JPG" alt=""></p>
<p><strong>￮ 요약</strong></p>
<ul>
<li>값 자체의 타입을 구별하기 위해서 리터럴의 타입이 존재한다.</li>
<li>자세한 건 표를 참조</li>
<li>진법 표현도 가능 { 2진법 : 0b / 8진법 : 0 / 16진법 0x }</li>
<li>리터럴이 큰 경우 _로 단위를 구분할 수 있다.</li>
<li>타입이 존재하기 때문에 작은 타입의 변수에 큰 타입의 리터럴을 넣으면 컴파일 에러가 발생한다.</li>
</ul>
<br/>

<h4 id="5-string-문자열">5) String 문자열</h4>
<ul>
<li><code>“”</code>로 감싸면 문자열로 자동으로 인식한다. 
하지만 String은 응용 메서드가 포함된 클래스이다. 이때 다음 두 코드는 내부적으로 차이가 있다.</li>
</ul>
<pre><code class="language-java"> String name = new String(&quot;Java&quot;);
 String name = &quot;Java&quot;;</code></pre>
<br/>

<h3 id="형식화된-출력-printf">형식화된 출력 printf()</h3>
<ul>
<li>printf()는 지시자를 사용하여 변수의 값을 여러 가지 형식으로 변환해서 출력할 수 있다.
이때 지시자는 값을 어떻게 출력할 것인지 지정해주는 역할을 한다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/ho_c/post/9b6ab2b4-62bd-400b-9b08-cccdbfc37b29/image.JPG" alt=""></p>
<p><strong>지시자 응용</strong></p>
<ul>
<li><strong>지시자와 함께 접두사를 사용하여 응용할 수 있다. 다음 예시를 보자.</strong></li>
</ul>
<p><img src="https://velog.velcdn.com/images/ho_c/post/b9cc7263-0ca1-4f76-954f-c08a450fde1b/image.JPG" alt=""></p>
<h3 id="화면에서-입력받기-scanner">화면에서 입력받기 Scanner</h3>
<ul>
<li><p>**Scanner 클래스는 사용자로부터 동적으로 데이터를 입력 받는 기능이 있다.</p>
</li>
<li><p>*</p>
<pre><code class="language-java">Scanner scanner = new Scanner(System.in);
String input = scanner.nextLine();</code></pre>
</li>
<li><p>객체 생성 후, nexLine() 메서드로 변수의 값을 저장할 수 있다. 이 메서드는 다음 순서로 동작한다.
&emsp;&emsp;① 메서드 호출
&emsp;&emsp;② 입력 대기 상태 -&gt; 입력 완료(Enter)
&emsp;&emsp;③ 입력값을 문자열로 반환</p>
</li>
<li><p>추가적인 메서드들을 통해서 값의 타입을 문자열에서 변환할 수 있다.</p>
</li>
</ul>
<hr>
<h2 id="3-진법">3. 진법</h2>
<ul>
<li><p>인간은 연속적인 사고를 하지만, 컴퓨터는 이를 이산적으로 사고한다. 
즉, 컴퓨터와 인간은 사고 체계가 다르다. </p>
</li>
<li><p>인간은 10진법 개념에 익숙하지만, 컴퓨터는 2진법 체계로 이해하기 때문에 인간이 가진 정보를 컴퓨터에 전달하려면 2진법적으로 풀어서 설명해줘야 한다.</p>
</li>
</ul>
<br/>

<h3 id="10진법과-2진법">10진법과 2진법</h3>
<ul>
<li>물론 컴퓨터가 10진법으로 이해하면 전달이 편해지겠지만 컴퓨터의 신호, 곧 전기를 10단계로 나누어서 전달하는 것은 불안정하기 때문에 0(신호X), 1(신호O)로 전달하는게 더 안정적이다.</li>
</ul>
<blockquote>
<p>따라서 인간의 정보를 컴퓨터는 2진법으로 처리하여 저장한다.</p>
</blockquote>
<pre><code class="language-java">int age = 25; // age : 25 -&gt; age 11001</code></pre>
<br/>

<h3 id="비트bit와-바이트byte">비트(bit)와 바이트(byte)</h3>
<blockquote>
<p>위 코드를 보면 10진 숫자 25가 컴퓨터에서는 2진수인 _11001_로 저장된다. </p>
</blockquote>
<ul>
<li>저 숫자 하나 하나가 메모리를 차지한다 생각하면 10진법으로 2자리면 되는 걸, 2진수로 5자리나 차지하기 때문에 비효율적이다.
때문에 후술할 8진법과 16진법을 통해서 해당 공간을 더욱 효율적으로 사용한다.</li>
</ul>
<blockquote>
<p>이보다 앞서서 이해해야하는 것은 컴퓨터가 공간을 구분하는 단위이다.</p>
</blockquote>
<ul>
<li><p>$11001_{(2)}$의 한자리는 1bit이다. 그리고 1bit 8개를 묶어서 1byte라는 단위로 사용한다.</p>
</li>
<li><p>추가적으로 word라는 단위가 있는데 이는 <strong>‘CPU가 한 번에 처리할 수 있는 데이터 크기’</strong>를 의미한다.</p>
</li>
</ul>
<blockquote>
<p>그러면 n개의 비트로 몇 개 10진수 값을 표현할 수 있을까?</p>
<blockquote>
<p><strong>기본적으로 공식은 쉽다</strong>.</p>
</blockquote>
</blockquote>
<ul>
<li>값의 개수 : $2^n$</li>
<li>값의 범위 : 0 ~ $$2^{n-1}$$ </li>
</ul>
<p>당연한 얘기이겠지만 개수와 범위는 잘 파악해둬야 한다. 범위는 0을 포함하기 때문에 최대값에서 –1을 해줘야 한다.</p>
<br/>

<h3 id="8진법과-16진법">8진법과 16진법</h3>
<ul>
<li><p>앞서말한 것처럼 2진수로만 처리하면 자리가 길어진다. 곧 공간이 커진다. 
때문에 8진법과 16진법을 통해 공간을 효율적으로 사용해준다.</p>
</li>
<li><p>왜 8진법과 16진법인지 궁금하다면 답은 간단하다. 8은 $2^3$, 16은 $2^4$이다. 
따라서 2진수를 3자리 또는 4자리씩 끊어서 해당 진법으로 변환할 수 있다.</p>
</li>
</ul>
<br/>

<h3 id="2진수를-8진수-16진수로-변환"><strong>2진수를 8진수, 16진수로 변환</strong></h3>
<ul>
<li>각 자리별로 2진법의 단위에 따라 0$$_{ or }$$1 x $2^n$으로 곱한 뒤, 더해주면 변환된 진법의 수가 나온다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/ho_c/post/ecccf2fa-8558-452a-8934-5eeb1044baa4/image.JPG" alt=""></p>
<p><img src="https://velog.velcdn.com/images/ho_c/post/42916eb5-e3f2-47fb-9766-abad76ff05a8/image.JPG" alt=""></p>
<blockquote>
<p>1254$<em>{(8)}$ = 1 x $8^3$ + 2 x $8^2$ + 5 x $8^1$ + 4 x $8^0$ = 512 + 128 + 40 + 4 = 684
2AC$</em>{(16)}$ = 2 x $16^2$ + A(10) x $16^1$ + C(12) x $16^0$ = 512 + 160 + 12 = 684</p>
</blockquote>
<br/>


<h3 id="정수와-진법-변환">정수와 진법 변환</h3>
<br/>

<h4 id="10진수를-n진수를-변환">10진수를 n진수를 변환</h4>
<ul>
<li>n진수를 변환하는 방법은 간단하다. 
n으로 나눌 수 없을 때까지 ( n보다 작을 때까지 나눠주면 된다. ) 나눠준다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/ho_c/post/63a8810c-38f4-465c-ab7d-9499d388ea6e/image.JPG" alt=""></p>
<br/>

<h4 id="n진수를-10진수로-변환">n진수를 10진수로 변환</h4>
<ul>
<li>변환된 값을 곱해서 모두 더해주면 된다. 시작은 마지막 자리($n^0$)부터 시작한다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/ho_c/post/0534a35b-bd09-4680-a22b-5da3236762ff/image.JPG" alt=""></p>
<br/>

<h3 id="실수의-진법-변환">실수의 진법 변환</h3>
<h4 id="10진-소수점수를-2진-소수점수로-변환">10진 소수점수를 2진 소수점수로 변환</h4>
<blockquote>
<p>10진 소수를 2진 소수점수로 구하는 방식 역시 그리 어렵지 않다.</p>
</blockquote>
<p><strong>① 10진 소수에 2를 곱한다.</strong>
    &emsp;&emsp;$0.625$ x $2$ = $1.25$</p>
<p><strong>② 위 결과에서 소수부분만 가져다가 다시 2를 곱한다.</strong>
    &emsp;&emsp;$0.25$ x $2$ = $0.5$</p>
<p><strong>③ 위 두 과정을 소수부가 0이 될 때까지 반복한다.</strong>
    &emsp;&emsp;$0.5$ x $2$ = $1.0$</p>
<p>&emsp;&emsp;$0.625$ → $0.101_{(2)}$</p>
<p>다만 유의할 건 해당 작업이 길어지거나, 무한반복이 될 수 있다. 이 때문에 실수형에서 정밀도가 중요한 것이다.</p>
<br/>

<h4 id="2진-소수점수를-10진-소수점수로-변환">2진 소수점수를 10진 소수점수로 변환</h4>
<blockquote>
<ul>
<li>이 역시, 2진수를 10진수로 변환하는 방법처럼 곱해주고 더해주면 된다. 
다만 단위가 소수이기 때문에 $2^{-n}$으로 각 자리를 곱해준다는 것이다. 
(시작은 $2^{-1}$이다.)</li>
</ul>
</blockquote>
<p>$0.101(2)$ 
→ 1 x $2^{-1}$ + 0 x $2^{-2}$ + 1 x $2^{-3}$ 
&emsp;&emsp;= 1 X 0.5 + 0 x 0.25 + 1 x 0.125 = 0.625</p>
<br/>

<h3 id="음수의-2진-표현">음수의 2진 표현</h3>
<h4 id="msb를-사용한-표현">MSB를 사용한 표현</h4>
<blockquote>
<p><strong>그렇다면 2진수로 음수는 어떻게 표현할까?</strong></p>
</blockquote>
<ul>
<li><p>방법은 간단하다. n개의 비트가 표현할 수 있는 값의 개수는 $2^n$개, 범위는 $0$ ~ $2^{n-1}$까지니까 4bit면 0~15까지 표현이 가능하다.</p>
</li>
<li><p>여기서 4개의 비트 중 가장 좌측에 있는 비트(MSB)를 부호로 사용하게 되면, 음수까지 표현할 수 있다. 
즉, 실제 숫자는 4개의 비트 중 우측부터 3개만 표현하고 제일 좌측에 있는 비트가 부호를 표현한다. 
(0 : 양수 / 1 : 음수) </p>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/ho_c/post/e4ced70e-5f3f-40fb-8d19-712d86c89f7b/image.JPG" alt=""></p>
<p>그러나 이 방식의 문제점은 0이 2개나 존재한다는 것이다. 양수 0과 음수 0으로 말이다. 이러면 결국 표현할 수 있는 값을 하나 포기하는 꼴이다.</p>
<p>그러니 극한의 이익을 추구하는 우리는 다른 방법을 찾아냈는데, 바로 “2의 보수법”이다.</p>
<h4 id="2의-보수법">2의 보수법</h4>
<blockquote>
<ul>
<li>2의 보수법은 보수법과 자리올림(carry)으로 발생하는 비트를 버리는 방식을 활용해서 표현한다.</li>
</ul>
</blockquote>
<ul>
<li>먼저 n의 보수란 <strong>‘더했을 때, n이 되는 수’</strong>를 의미한다. 
예를 들어 10에 대한 7의 보수는 3이다. 이처럼 2의 보수도 더해서 2가 되는 수를 말한다. </li>
<li>다만 우리는 2진법의 관점에서 이를 보기 때문에, 더해서 2가 되는 수란 ‘$10_{(2)}$’, 곧 자리올림이 발생하고 0이 되는 수다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/ho_c/post/52422b34-01af-4790-b3e7-65219ddedc35/image.JPG" alt=""></p>
<p>위 수에서 $1001_{(2)}$은 9, $0111_{(2)}$은 7이다. 그래서 둘이 더하면 16이 나온다. 하지만 2의 보수법에서는 둘이 더해서 발생한 1(굵은 글씨)을 버린다. 이렇게 되면 실제로는 둘이 더함으로서 0이 되어버린 상황이 발생한다.</p>
<p>결과적으로 $0111_{(2)}$은 7이 아닌 –9이다. 이게 바로 2의 보수법이자, 컴퓨터가 뺼셈하는 하는 방식이다.</p>
<br/>

<h4 id="음수를-2진수로-표현하기">음수를 2진수로 표현하기</h4>
<ul>
<li>앞서 본 방식을 그대로 차용하여서 음수를 2진수로 표현하는데, 일단 부호를 보는게 아니라 절대값만 보면 된다.</li>
</ul>
<p>&emsp;&emsp; <strong>① 절대값만 차용</strong></p>
<p>&emsp;&emsp;&emsp;&emsp;$-9$ → $9$</p>
<p>&emsp;&emsp; <strong>② 2진수로 변환</strong></p>
<p>&emsp;&emsp;&emsp;&emsp;$9$ → $1001_{(2)}$</p>
<p>&emsp;&emsp; <strong>③ 2의 보수법으로 음수 표현</strong></p>
<p>&emsp;&emsp;&emsp;&emsp;$1001_{(2)}$ → $0111_{(2)}$</p>
<br/>

<ul>
<li><p>③번을 더 쉽게 하는 방식은 _<strong>‘1의 보수법’</strong>_을 사용하는 것이다. </p>
<blockquote>
<p>일단 2진법의 관점에서 진행한 것만 유의한다면, 1의 보수는 더해서 1이 되는 걸 의미한다. 그래서 1의 보수법은 0→1, 1→0으로만 변환하면 된다.</p>
</blockquote>
</li>
<li><p>그렇게 되면 $1001_{(2)}$의 1의 보수는 $0110_{(2)}$가 된다. 각 자리값마다 반전시켜준 것이기 때문이다. 여기서 더하게 되면 당연히 $1111_{(2)}$이 된다. </p>
</li>
<li><p>마지막으로 +1을 해주게 되면 자동으로 각 자리마다 올림이 발생한다.
따라서 2의 보수는 <strong>‘1의 보수 + 1’</strong>이다.</p>
</li>
</ul>
<hr>
<h2 id="4-기본형primitive-type">4. 기본형(primitive type)</h2>
<p>이번엔 기본형의 저장방식을 살펴보자</p>
<h3 id="논리형-boolean">논리형 boolean</h3>
<ul>
<li>논리형은 ‘boolean’ 하나로 저장되는 값은 true, false 두 개 밖에 없다. 
따라서 1bit만 사용된다. <strong>기본값은 ‘false’이며</strong>, 대소문자가 구분되므로 대문자 사용시 에러가 난다.</li>
</ul>
<h3 id="문자형-char">문자형 char</h3>
<ul>
<li>문자형도 ‘char’만 있지만, 앞서 말했듯 char도 실질적으로 정수형이다. <blockquote>
<p>즉, 컴퓨터에 저장되는 값은 문자가 아닌 숫자가 저장되는데, 이는 ‘부호 없는 정수형’으로 0~$2^16$-1까지 값을 지정할 수 있다.</p>
</blockquote>
</li>
</ul>
<p>여기서 char는 각 수에 대칭되는 문자 코드들이 있는데 대표적으로 ‘유니코드’가 있다.</p>
<p>유니코드의 문자는 그에 대칭되는 수가 있으며 char는 그 수를 저장하는 것이다.</p>
<pre><code class="language-java">char ch = ‘A’; // ch = 65</code></pre>
<p>그래서 char는 int형으로 형변환이 가능하다.</p>
<pre><code class="language-java">int code = (int)ch; // code = 65</code></pre>
<p>또한 16bit이기 때문에 값은 16진법으로 저장된다. 앞서 본 유니코드 65는 0x41로 우리가 10진수로 이해하기 쉽게 표현된다.</p>
<br/>

<p><strong>￮ 요약</strong></p>
<ul>
<li>char는 정수형이며, 그 수에 대칭되는 문자가 있다. </li>
<li>이를 표로 만든게 유니코드이다.</li>
<li>문자를 컴퓨터가 이진수로 변환하는 것을 ‘인코딩’ 반대를 ‘디코딩’이라고 한다.</li>
</ul>
<br/>

<h3 id="정수형">정수형</h3>
<ul>
<li>정수형의 기본값은 ‘int’타입이다. 전체 비트 중에서 가장 좌측은 부호를 의미한다.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/ho_c/post/b26b9d09-ccf7-4dc9-9b1a-a0d16b361765/image.JPG" alt=""></p>
<blockquote>
<p>최대값과 최소값은 $-2^{n-1}$ ~ $$2^{n-1}-1$$으로 알 수 있다. </p>
</blockquote>
<br/>

<h4 id="부호-없는-정수형의-오버플로우">부호 없는 정수형의 오버플로우</h4>
<ul>
<li>각 타입마다 해당된 만큼 비트를 가지고 있다. 
이때 그 비트를 벗어나는 경우, 즉 자리 올림이 발생하면 오버플로우가 일어난다.</li>
</ul>
<blockquote>
<p>오버플로우는 다음과 같다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/ho_c/post/6cc302ab-9b3f-4d3b-9125-84c748172905/image.JPG" alt=""></p>
<h4 id="부호-있는-정수형의-오버플로우">부호 있는 정수형의 오버플로우</h4>
<ul>
<li>부호가 없는 정수가 최대값을 넘어서게 되면 발생하는 것과 달리, 부호가 있을 땐 그 시점이 “부호 비트가 변경될 때 발생한다.”
곧 비트의 자리 올림이나, 내림이 발생하게 되면 발생한다는 것이다. </li>
</ul>
<blockquote>
<p>2의 보수법으로 표현된 이진수를 보면 다음과 같다.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/ho_c/post/fd7f0565-c40d-42d4-912a-533217a79ff1/image.JPG" alt=""></p>
<br/>

<h3 id="실수형">실수형</h3>
<ul>
<li><p>앞에서부터 계속 거론했듯이, 실수형의 포인트는 ‘정밀도’이다.
즉, 소수점 이하 몇 번째까지 정확하게 표현하는지가 중요한 것이다.</p>
</li>
<li><p>이 정밀도가 중요하게 된 이유는 앞서 본 10진법 소수를 2진법 소수를 바꾸는 과정 때문에 발생한다. 그 과정에서 무한하거나, 아니면 타입의 저장공간보다 길어질 수 있기 때문에 이를 이진수로 변환하는 과정에서 정확하게 저장하는 것이 중요하다.</p>
</li>
</ul>
<blockquote>
<p><em><strong>참고로 실수형에서는 오버플로우가 발생하면 순환이 아닌, 무한대가 되며, 양의 최소값보다 작은 값이 되는 경우 0이 된다.</strong></em></p>
</blockquote>
<h4 id="실수형의-저장형식">실수형의 저장형식</h4>
<ul>
<li>실수는 비트를 <strong>부호, 지수, 가수</strong> 이렇게 3가지로 구분하여서 저장한다. 
정수형과 다르며, 실제 저장되는 형태는 2진법 형태라는 걸 유의하며 보자.</li>
</ul>
<p><img src="https://velog.velcdn.com/images/ho_c/post/53e9a0b2-f48a-4cbb-9571-26f69d7ba94c/image.JPG" alt=""></p>
<p><strong>① 부호부(S)</strong></p>
<ul>
<li>정수와 똑같이 0은 양수, 1은 음수이다.<br/>

</li>
</ul>
<p><strong>② 지수부(E)</strong></p>
<ul>
<li>각 단위가 2의 지수인 ‘2의 제곱을 곱한 형태(가수 x $2^{지수}$)’로 저장하기 때문에 지수를 나타내야 한다. 이 지수는 부호가 있는 수이며, 계산 방식은 bias을 이용해서 음수와 양수를 나타낸다.</li>
</ul>
<blockquote>
<p><strong>bias?</strong>
먼저 8bit가 표현 가능한 수의 개수는 256($2^8$)개이며, 범위는 0~255이다. 그러나 지수는 정규화에 따라 양의 지수, 음의 지수로 구분될 수 있다. 하지만 지수부에는 부호가 따로 존재하지 않기 때문에 bias를 사용한다.</p>
</blockquote>
<ul>
<li>즉, 0~255의 중간값(-127, 128 제외, 예약된 값이 있음)인 127를 기점으로 음수와 양수가 나뉜다. 그래서 저장할 때는 실제 지수 +127을 해준 다음, 2진수로 변환하여 저장한다.<blockquote>
</blockquote>
결과적으로 지수를 나타내기 위해선 지수부에 입력된 값에 –127을 해줘야 해당 지수가 나타난다.</li>
</ul>
<p>&emsp;&emsp; $01111111_{(2)}$ =&gt; 127-127 = $2^0$</p>
<br/>

<p><strong>￮ 요약</strong></p>
<ul>
<li>부호가 없기 때문에 bias를 통해서 지수의 부호를 구분한다.</li>
<li>$-127$과 $128$은 예약된 값이 있어서 범위에서 제외된다.</li>
<li>$-127$ : 0 /  $128$ : Infinity(무한)</li>
<li>지수가 0을 표현할 때는 –127으로 가수부도 모두 0으로 채운다.</li>
<li>double의 bias는 1023이다.<blockquote>
<p><strong>bias시뮬레이션 <a href="https://www.h-schmidt.net/FloatConverter/IEEE754.html">https://www.h-schmidt.net/FloatConverter/IEEE754.html</a></strong></p>
</blockquote>
</li>
</ul>
<br/>

<p><strong>③ 가수부</strong></p>
<ul>
<li><p>가수부는 실제 값을 저장하는 부분으로 23bit로 7자리의 10진수를 저장할 수 있다. 
그리고 7자리의 10진수가 float의 정밀도를 나타낸다. 
때문에 float의 2배 정도 가수부가 큰 double의 정밀도가 15자리가 되는 것이다.</p>
</li>
<li><p>그러나 실수 중에는 파이 같은 무한 소수, 또는 비트보다 긴 2진수 소수가 나올 수 있다. 
때문에 가수부 저장은 “정규화”라는 과정을 거친다.</p>
</li>
</ul>
<blockquote>
<p>&emsp;&emsp;$9.1234567$ → $1001.000111111001101011011011...$</p>
</blockquote>
<ul>
<li><p>위 수가 비트를 넘기는 수 중에 하나이다. 
이를 저장하기 위해서 정규화를 사용하면 실수가 1로 시작하도록 소수점을 앞으로 옮긴다.. 
이때 당기는 횟수를 ‘지수’라고 생각하는 편할 것이다.</p>
<blockquote>
<p>지수가 양수일 수록 큰 수, 음수일 수록 0에 가깝다.</p>
</blockquote>
</li>
<li><p>그래서 총 3번 옮겨야되기 때문에 정규화되면 다음과 같이 표현할 수 있다.</p>
</li>
</ul>
<p>&emsp;&emsp;$1.00100011111100110101101...$ x $2^3$</p>
<ul>
<li>여기서 실수의 시작은 무조건 1이기 때문에 소수점 이하 23자리는 모두 저장할 수 있다.<br/>

</li>
</ul>
<p><strong>￮ 요약</strong></p>
<ul>
<li>실수형에는 오차가 존재하는데, 버려지는 비트의 시작이 1일 경우 반올림이 발생하기 때문이다.</li>
</ul>
<hr>
<h2 id="5-형변환">5. 형변환</h2>
<h3 id="형변환캐스팅이란">형변환(캐스팅)이란?</h3>
<blockquote>
<ul>
<li>형변환이란, 변수 또는 상수의 타입을 다른 타입으로 변환하는 것</li>
</ul>
</blockquote>
<ul>
<li>모든 변수와 리터럴에는 타입이 존재한다. 
하지만 실제로 보면 타입에 상관없이 연산이 해야되는 경우도 존재한다.
그래서 연산 수행 전에 타입을 일치시켜야 하는데 이를 ‘형변환’이라고 한다.</li>
</ul>
<br/>

<h3 id="형변환-방법">형변환 방법</h3>
<ul>
<li>형변환 대상 앞에 (타입)을 붙여주면 된다. 물론 이 때, 저장된 값 자체는 변하지 않는다.</li>
</ul>
<pre><code class="language-java">double di = 85.4;
int score = (int)d; </code></pre>
<br/>

<p><strong>￮ 요약</strong></p>
<ul>
<li>boolean 타입을 제외하곤 기본형의 모든 형은 변환이 가능하다.</li>
<li>실수형에서 정수형(int)로 형변환하면 소수점 이하는 버려진다. 단, 반올림은 발생하지 않는다.</li>
</ul>
<br/>


<h3 id="정수형-간의-형변환">정수형 간의 형변환</h3>
<ul>
<li>사실 작은 타입을 큰 타입으로 변환하는 건 딱히 상관이 없다. 
산술변환이 일어나 자동으로 타입을 처리하기 때문이다.
다만 문제는 큰 타입을 작은 타입으로 변환할 때 일어나는데, 그 이유는 저장공간의 크기가 다르기 때문이다.<br/>


</li>
</ul>
<p><strong>￮ 요약</strong></p>
<ul>
<li>작은 타입에서 큰 타입으로 변환하는 경우, 남는 비트는 음수이면 1로 양수면 0으로 채운다.</li>
</ul>
<br/>


<h3 id="실수형-간의-형변환">실수형 간의 형변환</h3>
<ul>
<li>실수형 역시 정수형과 동일하게 큰 타입을 작은 타입에 집어넣으면 값 손실이 일어난다. 
때문에 애초에 넣을 때, 큰 곳에 넣어주는 것이 정확한 값을 저장할 수 있다.</li>
</ul>
<blockquote>
<p>아래 코드 같은 경우, 형변환을 했지만 이미 값을 집어넣으면서 오차가 발생했기 때문에 형변환을 하더라도 값이 바뀌지 않는다.</p>
</blockquote>
<pre><code class="language-java">float f = 9.1234567f; // 9.123456954956055000
double d = 9.1234567; // 9.123456700000000000
double d2 = (double)f; // 9.123456954956055000</code></pre>
<p>앞서 말한 버려진 비트에서 반올림이 일어난 경우이다.</p>
<h3 id="정수형과-실수형간의-형변환">정수형과 실수형간의 형변환</h3>
<br/>

<h4 id="정수-→-실수">정수 → 실수</h4>
<ul>
<li>정수를 실수를 바꾸는 것은 간단하다. 
정수를 2진수로 바꾼 뒤, 정규화 후 그대로 실수의 저장 방식에 따라 저장하면 된다.
다만 2진수로 바뀌는 과정에서 비트가 버려지면 반올림이 발생해 정밀도가 떨어지게 될 수도 있다는 것만 주의하자.</li>
</ul>
<br/>

<h4 id="실수-→-정수">실수 → 정수</h4>
<ul>
<li>실수를 정수로 바꾸면, 소수점 이하를 버려버린다. 
애초에 저장방식이 다르니 당연한 얘기이다. 
이때 중요한 것은 버려질 때, 반올림은 발생하지 않는다.</li>
</ul>
<p>&emsp;&emsp; $1.00100011111100110101101$ x $2^3$ → $1001.00011111100110101101$ → $9$</p>
<blockquote>
<p>방식은 역정규화를 한 다음 소수점 이하는 버리고, 정수부분만 저장한다.</p>
</blockquote>
<br/>

<h3 id="자동-형변환">자동 형변환</h3>
<ul>
<li>자동 형변환이 앞서 말한 산술변환이다. 
편의상 제공하는 기능으로 컴파일러가 생략된 형변환을 추가해서 진행한다.</li>
</ul>
<ul>
<li><p>조건은 작은 타입에서 큰 타입으로 변환할 때만 지원한다. 
반대에 경우 형변환 연산자<code>(타입)</code>을 생략하면 에러가 발생한다.</p>
</li>
<li><p>특히 연산 과정에서 큰타입과 작은타입을 연산할 때, 자동으로 작은 타입을 큰 타입으로 변환하기 때문에 ‘산술 변환’이라 한다.</p>
</li>
</ul>
<pre><code class="language-java">int i = 3;
double d = 1.0 + i

→ double d = 1.0 + i;
→ double d = 1.0 + (double)i;
→ double d = 1.0 + (double) 3; 
→ double d = 1.0 + 3.0; 
→ double d = 4.0; </code></pre>
<br/>

<p><strong>￮ 요약</strong></p>
<ul>
<li>컴파일러는 기존의 값을 최대한 보존할 수 있는 타입으로 자동 형변환한다.</li>
<li>기본형과 참조형은 서로 형변환이 불가능하다.</li>
<li>작은 타입에서 큰 타입 변환은 형변환을 생략할 수 있다.</li>
</ul>
<br/>

<hr>
<blockquote>
<p>도움이 되셨다면 <strong>&#39;좋아요&#39;</strong> 부탁드립니다 :)</p>
</blockquote>
]]></description>
        </item>
        <item>
            <title><![CDATA[국비교육 파이널 프로젝트 회고 ]]></title>
            <link>https://velog.io/@ho_c/%EA%B5%AD%EB%B9%84%EA%B5%90%EC%9C%A1-%EC%B0%AC%EB%9E%80%ED%96%88%EB%8D%98-%EA%B5%AD%EB%B9%84%EA%B3%BC%EC%A0%95%ED%8C%8C%EC%9D%B4%EB%84%90-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%ED%9A%8C%EA%B3%A0</link>
            <guid>https://velog.io/@ho_c/%EA%B5%AD%EB%B9%84%EA%B5%90%EC%9C%A1-%EC%B0%AC%EB%9E%80%ED%96%88%EB%8D%98-%EA%B5%AD%EB%B9%84%EA%B3%BC%EC%A0%95%ED%8C%8C%EC%9D%B4%EB%84%90-%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%ED%9A%8C%EA%B3%A0</guid>
            <pubDate>Tue, 30 Aug 2022 12:44:54 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>지난 7월 25일 길고 길었던 2월 14일부터 시작된 110일 간의 여정이 끝났다.
수료하자마자 8월은 코드의 “코”도 보기가 싫어서 신나게 쉬었다.</p>
</blockquote>
<p>는 내 바램이고, 사실 2주 정도 여행도 가고 쉴 생각이었는데, 여행 대비해서 백신 맞은지 2일만에 코로나에 걸려버렸다. 후유증이 꽤 심해서 8월 나머지 기간은 운동도 못하고 방에서 약만 먹은 것 같다.</p>
<p>그래서 8월이 하루 남은 오늘 드디어 미루고 미뤘던 “파이널 프로젝트” 회고를 한다. <strong>(이 글이 누군가에게 도움이 되길 바라며)</strong></p>
<hr>
<h2 id="1-팀">1. 팀</h2>
<p>세미가 끝난 당일 같이 수업을 듣는 분으로부터 함께하자는 제의가 왔다. 파이널까지 한 달 정도 남아서, 팀 만들 생각도 없었는데 다른 분들은 미리 사람들을 모았다. 다행히 그 덕분에 같이 할 사람 구하는 수고는 줄였다.</p>
<p>함께 프로젝트를 하는 분들은 반에서 실력으로 날아다니시는 분들이었다. 물론 나도 날아다니긴 했지만, 세미 때 아쉬움을 충분히 극복할 수 있겠다 싶어 뒤도 안 돌아보고 덥썩 물어버렸다. </p>
<p>다행히 팀원분들은 모두 좋은 분들이었다. 일단 서로 실력이 뒷받침되기 때문에 홀로 고독하게 문제를 해결하지 않고 공유하면서 프로젝트를 진행해나갔다. </p>
<hr>
<h2 id="2-프로젝트-설계">2. 프로젝트 설계</h2>
<p>프로젝트 설계는 팀이 만들어지고 4일 뒤부터 바로 시작한 것 같다. 일단 협업툴로 디스코드, 노션, 미로(Miro), Exerd를 사용했다. </p>
<p>디스코드는 비대면 상황이 많으니 회의 용도로 사용했고, 회의 과정에서 만들어지는 서류들은 노션으로 공유하여 작성하였다.</p>
<p>거의 일주일 가량 6시간씩 회의를 해서 최종 채택된 아이디어는 공유 경제를 기반한 “재능 공유 플랫폼”이었다. </p>
<p>사실 클래스101, 숨고를 조합한 사이트가 솔직한 표현이다. 이번 프로젝트에서 나는 회원가입-소셜 로그인 기능, 고객센터 페이지를 담당했다.</p>
<hr>
<h2 id="3-담당-기능">3. 담당 기능</h2>
<h3 id="1-모듈화">(1) 모듈화</h3>
<p>다른 분들은 모르겠지만, 장고로 웹 서비스를 만들어봤던 나는 기존에 JSP를 가지고 주먹구구식으로 사이트를 만드는 것에 불만이 많았다. 그래서 파이널에선 좀 더 “유지 보수”가 편리한 코드를 짜보고 싶었다.</p>
<p>이러한 연유로 전체 사이트의 Header, Footer를 <code>&lt;jsp:include page=&quot;&lt;%=variable%&gt;&quot; flush=&quot;true&quot;/&gt;</code>를 사용해서 모듈화하였다. 이걸 모듈화라고 표현하는 것도 우습긴 하지만 세미 때 경험으로 비춰볼 때는 장족의 발전이었다.</p>
<h3 id="2-modal">(2) Modal</h3>
<p>로그인, 회원가입 페이지를 따로 만들지 않고, Ajax를 이용하여 부트스트랩 모달창에서 기능이 동작되도록 구현하였다.</p>
<p>!youtube[2MzKtmAzZS8]</p>
<h3 id="3-sns-로그인-기능">(3) SNS 로그인 기능</h3>
<p>내 파트의 핵심은 카카오 API, 네이버 로그인 API였다. 사실 프로그래밍을 시작하면서부터 API는 나를 괴롭히던 개념이었고, 한번쯤은 직접 몸소 경험하고 싶었다. 그래서 파이널에는 내가 API를 적용시켜보겠다고 했다.</p>
<p>이 과정에서 API의 개념은 좀 더 공부할 수 있었다. 그동안 나를 괴롭히던 건 Http API가 아니였단 사실과 함께 막상 만들어진 문서를 읽으면서 API를 쓴다는게 꽤 재밌는 일이라는 것과 API 문서의 중요성을 알게 되었다. 특히 정말 쉽게 써주신 카카오에게 감사의 말씀을 전하고 싶다.</p>
<p>!youtube[1ykCeGdINLw]</p>
<p>무튼 카카오 로그인 API는 프론트 단에서 SDK를 통해서 쉽게 동작할 수 있도록 만들었다. 그래서 네이버도 쉬울 줄 알았는데, 막상 네이버는 자바스크립트로 프론트 단에서 API를 사용하기 어려워서 포기했다.</p>
<p>그래서 돌고 돌아 결국 구글 API로 갔는데, 여기는 또 문제가 세미 때까지 지원되던 기능이 파이널 기간에는 전부 변경되었다는 것이다! 두둥....결국 눈물을 흘렸지만, 기존에 구현하신 분들에게 여쭤봐서, JWT를 통해서 구현했다. <strong>(웬만하면 서버 쪽에서 만지기 싫었는데, 구글은 그래야 하더라..)</strong></p>
<p>그래도! JWT를 공부하는 좋은 기회가 되었다. 물론 실제 배포 시에 구글은 테스트용만 지원해줘서 난항을 겪었지만 <del>프로젝트 용도로 쓰지마여...골치 아파</del></p>
<h3 id="4-고객센터">(4) 고객센터</h3>
<p>!youtube[5PUbBpT8USs]</p>
<p>사실 고객센터는 단순한 게시판 CRUD 구현이었다. 대댓글 기능은 넣지도 않았고, 간단한 프론트 페이지랑 그 외에 1대1 문의, 공지사항 작성 정도로 구현했다. 앞에서 시간을 많이 소요했다보니 이 파트에는 그다지 큰 시간을 투자하지 못한게 아쉽다.</p>
<p>하지만 한 가지 배운 점은 글 작성 시, input을 <code>&lt;div contenteditable=&quot;true&quot;&gt;</code>로 만들어 정보를 <code>submit</code> 이벤트 시, <code>&lt;input type=&quot;hiden&quot;&gt;</code>으로 옮겨 DB에 넣는 방식으로 구현했는데 이때, XSS(Cross-Site Scripting) 공격에 대한 취약점이 발견됐다. </p>
<p>말 그대로 입력창에 스크립트를 넣으면 그게 그대로 적용되는 것이었다. 이는 프로젝트 후반부에 테스트 케이스를 만들면서 발견된 부분인데, 이 부분을 보완하기 위해서 JSTL의 <code>&lt;c:out&gt;</code>을 이용하여 프론트에 출력되도록 하였다.</p>
<p>이 과정에서 내가 만든 기능이 가지는 <strong>&#39;보안 취약성&#39;</strong>을 직접 눈으로 확인하게 되었고, 애초에 설계 과정에서 기능 구현이 우선순위가 아닌, 사용자와 서비스 제공자 모두에게 <strong>&#39;안전한 서비스&#39;</strong>를 만들기 위해 보안을 공부해야되겠다고 다짐했다.</p>
<h3 id="5-테스트-케이스">(5) 테스트 케이스</h3>
<p>프로젝트를 마무리하면서 발표 3일 전부터는 테스트를 거쳤다. 각 조별로 버그나 아쉬운 점을 피드백하는 ‘크로스 체킹’ 시간을 강사님이 마련해줬는데, 우리 조만 참여하고 다른 조들은 참여하지 않는다고 해서 팀원들끼리 테스트 케이스를 만들어 점검했다.</p>
<p>!youtube[JQfk2a5ia9w]</p>
<h3 id="6-시연-영상">(6) 시연 영상</h3>
<p>이왕 만든 거 시연을 영상으로 만드는 게 좋을 것 같아 직접 만들었다.</p>
<p>!youtube[DXGGVG27sRU]</p>
<hr>
<h2 id="4-아쉬운-점">4. 아쉬운 점</h2>
<blockquote>
<p>사실 이 글의 본제는 “아쉬운 점”이다. 프로젝트 자체보다는 국비 학원 과정 자체에 대한 아쉬운 점이었다.</p>
</blockquote>
<h3 id="1-스프링-학습-기간이-매우-짧다">(1) 스프링 학습 기간이 매우 짧다.</h3>
<p>세미 후, 파이널까지는 길면 4주 정도의 시간을 준다. 워킹데이로는 겨우 20일 정도.
근데 이 기간에 스프링 + Mybatis + 단위테스트 이렇게 세 가지를 학습한다. 그래서 Mybatis 2일, 단위테스트 1일, 스프링 프레임워크 10일 정도? 나머지는 나라에서 부여한 시험을 보는 날이 제외된다.</p>
<p>그 과정도 JSP로 만들었던 게시판 CRUD를 스프링으로 만드는 것이다. 설치만 하루를 쓴다.</p>
<p>물론 기존에 만든 게시판을 스프링으로 만드는 것이 좋은 학습 방법이겠지만 이 과정에서 쓰는 어노테이션도 끽해야 7개 정도, 사실 스프링 프레임워크 자체를 이해하는 시간은 주어지지 않는다.</p>
<p>강사님도 아무것도 모르는 우리가 실무자를 따라가기 위해 스프링의 힘을 빌리는 과정이라고 설명하신다. 그래서 DI, IOC, AOP(하지도 않음), Soild 같은 스프링의 근본을 배울 순 없다.</p>
<p>이건 전부 다 수강생 본인의 역량이다. 혼자 유튜브에서 강의를 찾아 들어야 한다. 그래서 나는 DI, IOC를 위해 김영한님과 뉴렉처 유튜브를 계속 봤다.</p>
<h3 id="2-결국-파이널의-질이-떨어진다">(2) 결국 파이널의 질이 떨어진다.</h3>
<p>스프링 기반 프로젝트인데, 그 원리를 이해하고 있지 않으니 파이널 프로젝트에서 스프링의 역할은 그냥 조립해주는 기계에 불과하다. (뭐 그렇긴 하지만 나는 좀 더 깊게 쓰고 싶었다.)</p>
<p>스프링 시큐리티 같은 건 구경도 못했다. 물론 스프링 MVC를 이해하는 좋은 기회이긴 했지만, 스프링을 이해하고 쓰기보단 알려준 거 그대로 기능 구현만 목적으로 코드를 짜는 프로젝트였다.</p>
<p>그 결과, 유지 보수는 어려워졌다. 각 객체 간의 의존성은 꼬일대로 꼬였고, 디자인 패턴은 거의 개무시했다. 그냥 있는 거 그대로 쓰는 정도... 그런 점에서 개발자로서 설계 단계에서 오류가 있었고, 이를 극복하지 못했다는게 아쉽다.</p>
<p>또 나의 경우 로그인 기능을 구현했는데, 막상 보니 암호화 된 비밀번호를 DB랑 대조해서 맞으면 그게 “로그인?” 기능이라 하는 것도 웃겼다. 그래서 찾아보니 이 기능에도 “인증과 인가”라는 개념이 존재했다.</p>
<p>돌아보니 세션으로 구현한 “인가”는 엔터프라이즈 급에서는 서버 부하와 로드 밸런싱의 한계가 있었다. 만약 시간이 좀 더 주어졌다면, 이런 부분까지 고려해서 JWT로 한번 구현해보는 것도 좋았을 텐데 라는 아쉬움이 남는다.</p>
<h3 id="3-이거-백엔드-과정-맞음">(3) 이거 백엔드 과정 맞음?</h3>
<p>백엔드가 탄탄하지 않으니, 결국 투자하는 건, 스프링이 아니라 프론트다. 우리 팀도 프론트 페이지를 구현하느라 총 4주 동안 3주 이상을 허비하였다.</p>
<p>나도 하면서 백엔드 만지는 시간보다 프론트가 더 오래 걸리더라. 물론 포토폴리오로 쓰기 위해서 이쁘고 세련되게 꾸미는 것도 좋지만, 막상 SQL쿼리부터 백엔드 로직이 엉망이었단 걸 생각하면 이걸 백엔드 포폴로 쓸 수 있을까 라는 고민이 든다.</p>
<hr>
<h2 id="5-마치며">5. 마치며</h2>
<p>무튼 5.5개월의 국비 과정이 끝났다. 비대면이라 많이 친해지지 않았지만, 그래도 좋은 사람들과 함께 공부했고, 좋은 강사님을 만난 건 다행이라고 생각한다.</p>
<p>또 국비 과정을 통해서 웹 개발이라는 가장 큰 구조를 배웠다고 생각한다. 이제 취업 준비를 위한 준비가 끝났으니, 취업을 위해 달려보자.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Day 89. Spring Framwork 9 : Transaction, Intercepter]]></title>
            <link>https://velog.io/@ho_c/%EA%B5%AD%EB%B9%84%EA%B5%90%EC%9C%A1-Day-89</link>
            <guid>https://velog.io/@ho_c/%EA%B5%AD%EB%B9%84%EA%B5%90%EC%9C%A1-Day-89</guid>
            <pubDate>Sun, 26 Jun 2022 10:50:30 GMT</pubDate>
            <description><![CDATA[<p>드디어 마지막 수업이다. 이제 파이널 프로젝트만 하면 국비 학원 이야기도 엔딩을 본다. 
사실 이제 현 과정 내에서 배울만한 건 거의 다 배워서...오늘은 2가지 처리만 알아보자.</p>
<h2 id="1-서비스-레이어-transaction-처리">1. 서비스 레이어 Transaction 처리</h2>
<h3 id="1-개요">1) 개요</h3>
<p>트랜잭션이 무엇인가? 알다시피 DB에 입력되는 정보는 바로 처리되지 않는다. DB 내부에 들어가기 전, 트랜잭션이라는 일종의 ‘중간 저장소’이다. 
즉, 데이터의 안정적 처리를 위해 DB 접근을 한 번 막아주는 것이다. 그래서 커밋을 통해서 우리가 진행한 작업을 DB에 한 번에 반영한다.</p>
<p>이때, 백엔드에 서비스 계층이 없다면 Transaction 처리를 컨트롤러에서 해야 한다. 근데 DB 작업을 컨트롤러에서 하는 건, 패턴을 벗어나고, DAO는 동시에 2개의 작업을 진행 할 수 없다.</p>
<blockquote>
<p>보다 직관적으로 보기 위해 Transaction 처리를 만들어보자. </p>
</blockquote>
<p>글쓰기 기능에서 사용자는 글 하나이지만 실제 백에서는 글 입력과 파일 정보 입력이 따로 들어간다. 이때 발생하는 게 “원자성 이슈”이다.</p>
<pre><code class="language-java">public void insert(MessagesDTO dto) throws Exception {
    dao.insert(dto); 
    fdao.insert(new FilesDTO(0, &quot;ori&quot;, &quot;sys&quot;, 0));     
}</code></pre>
<p>무슨 일이 있든 사용자는 하나의 작업으로 보기에, 위 두 메서드는 함께 실패하거나 함께 성공해야만 한다.</p>
<p>만약 두 작업 중 앞쪽이 실패하면 자동으로 예외 처리되어 원자성을 지키는데, 앞에는 성공하고, 뒤에서 예외가 나면 이미 앞의 작업은 처리되었기에 원자성 문제가 발생한다.</p>
<p>결국, 경우의 수는 둘 다 성공하던가, 하나가 실패하면 성공한 것은 트랜잭션이 롤백 돼야 한다. 이걸 MVC 2은 Connection 객체를 직접 만져서 rollback()을 호출해서 둘의 상태를 맞췄지만, 스프링에서는 트랙잭션 처리 인스턴스를 제공한다.</p>
<hr>
<h3 id="2-transcationmanager-빈-생성">2) transcationManager 빈 생성</h3>
<p>트랜잭션 처리를 스프링이 해주기 위해선, 해당 처리를 담당하는 인스턴스를 만들어 줘야 한다. 생성 방식은 생성자로 하며, 인자값으로 DBCP 인스턴스를 DI해준다.</p>
<p><em><strong>[ root-context.xml ]</strong></em></p>
<pre><code class="language-xml">&lt;bean id=&quot;transactionManager&quot; class=&quot;org.springframework.jdbc.datasource.DataSourceTransactionManager&quot;&gt;
    &lt;constructor-arg name=&quot;dataSource&quot; ref=&quot;dataSource&quot;/&gt;
&lt;/bean&gt;</code></pre>
<hr>
<h3 id="3-어노테이션-처리">3) 어노테이션 처리</h3>
<p>이 역시, 어노테이션으로 처리한다. 그래서 트랜잭션 처리가 필요한 곳을 어노테이션으로 표기해주면 해당 메서드의 Connection 객체는 트랜잭션 처리가 가능하다.</p>
<pre><code class="language-java">@Transactional
public void insert(MessagesDTO dto) throws Exception {
    dao.insert(dto); 
    fdao.insert(new FilesDTO(0, &quot;ori&quot;, &quot;sys&quot;, 0)); 
}</code></pre>
<hr>
<h3 id="4-servlet-contextxml">4) servlet-context.xml</h3>
<p>아쉽게도 @Transactional 은 컴포넌트 스캔이 안되서, 직접 올리도록 추가해줘야 한다.</p>
<h4 id="①-namespace-import">① namespace import</h4>
<p>이를 위해서 이클립스에선 하단 콘솔 부분에서 Namespace 이동해, tx를 체크해서, 문법 정보를 추가해준다.</p>
<p><img src="https://velog.velcdn.com/images/ho_c/post/b3735aef-2c8c-40a0-bd63-1a9b1cb66497/image.JPG" alt=""></p>
<h4 id="②-annotation-driven-추가">② annotation-driven 추가</h4>
<p>이제, xml이 분석되면서 트랙잭션 어노테이션으로 표기된 것을 메모리로 올려 스프링 풀에 넣어줘야 한다.</p>
<pre><code class="language-xml">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;beans:beans xmlns:tx=&quot;http://www.springframework.org/schema/tx&quot;
    xsi:schemaLocation=&quot;http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd
        http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd&quot;&gt;

    &lt;tx:annotation-driven/&gt;

&lt;/beans:beans&gt;</code></pre>
<p>이제 우리 프로그램에서 SQL Exception이 발생하면 스프링이 트랜잭션 처리를 진행해준다.</p>
<hr>
<h2 id="2-interceptor--로그인-인증">2. Interceptor : 로그인 인증</h2>
<p>여태 우리는 JSTL/EL로 세션의 로그인 키의 유무에 따라 UI 보이고, 안보이고 하는 걸 로그인 인증인 줄 알고 있었지만, 그건 로그인 인증이 아니다.</p>
<p>그래서 가장 간단하게 로그인 인증을 하는 법은 컨트롤러에서 로그인 시, 접근 가능한 페이지로 가는 모든 맵핑에서 그 유무를 검사하면 된다. </p>
<blockquote>
<p>근데 이게 처리는 쉬운데 생각해보면 그런 맵핑이 한 두 개일까? 가능한 얘기지만 매우 비효율적인 방식이다.</p>
</blockquote>
<p>결국 우리가 택할 방법은 이 로그인 인증을 한번에 해주는 ‘계층’ 곧, 레이어를 만들어 주는 것이다. 쉽게 보면 우리가 인코딩 방식을 맞추기 위해서 건드린 servlet filter을 구현해주는 것이다.</p>
<p>일단, 클라이언트의 요청이 컨트롤러에 도달할 때까지, Tom-DS(DispatcherServlet)를 거치고, 이 사이의 필터를 servlet filter (톰캣 주관)라 한다. 우리가 만들 계층은 DS – Controller(handler) 사이에 위치하는데, 여기 오는 계층들을 interceptor라고 한다. </p>
<blockquote>
<p><strong>이 인터셉터 클래스는 스프링 예하에 있어 스프링 요소를 사용하기 쉽다는 이점이 있다.</strong></p>
</blockquote>
<p>돌아보면 모든 클라이언트의 요청은 DS를 거쳐서 컨트롤러에게 간다. 따라서 그 사이에 있는 인터셉터는 모든 request를 받게되는데, 여기에 로그인 아이디가 없으면 돌려보내면 되는 것이다. </p>
<hr>
<h4 id="-사용법-">[ 사용법 ]</h4>
<h3 id="1-패키지-클래스-생성">1) 패키지, 클래스 생성</h3>
<p>일단 어디서나 패키지 만들고, 클래스 만드는 건 기본이다. 그리고 인터셉터는 컨트롤러 하나 당 한 개씩 만들어지기 때문에 웬만하면 패키지로 관리하는게 편하다.</p>
<hr>
<h3 id="2-handlerinterceptor-상속-override">2) HandlerInterceptor 상속, Override</h3>
<p>인터셉터 클래스는 HandlerInterceptor 인터페이스를 상속받아, 오버라이딩한다. </p>
<pre><code class="language-java">public class LoginValidator implements HandlerInterceptor{

    @Autowired
    private HttpSession session;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) // DS -&gt; controller
            throws Exception {

        String loginId =  (String)session.getAttribute(&quot;loginID&quot;);

        if(loginId != null) {
            return true; // 통과
        } 

        response.sendRedirect(&quot;/error&quot;); // error요청으로 재요청하여 페이지 전환
        return false; //  요청 취소        
    }
}</code></pre>
<p>일단 다루는 대상이 session 정보이기 때문에 DI를 기본으로 해준다. 그 다음 오버라이딩할 메서드에 다음 로직을 세워준다.</p>
<ul>
<li>세션에 로그인 키가 존재하면 요청을 승인</li>
<li>없다면, 응답으로 error로 재요청하게 하고, 기존 요청은 취소시킨다.</li>
</ul>
<hr>
<h3 id="3-interceptor-빈-등록">3) interceptor 빈 등록</h3>
<p>interceptor은 따로 어노테이션을 하지 않기 때문에, 우리가 <code>servlet-context.xml</code>에서 직접 생성하도록 세팅해줘야 한다.</p>
<pre><code class="language-xml">&lt;interceptors&gt;
    &lt;interceptor&gt;
        &lt;mapping path=&quot;/**&quot;/&gt; &lt;!--resource 맵핑이랑 비슷한 개념 --&gt;            
        &lt;beans:bean class=&quot;kh.spring.interceptor.LoginValidator&quot;/&gt;

    &lt;/interceptor&gt;
&lt;/interceptors&gt;</code></pre>
<p>보다시피 인터셉터는 <code>&lt;interceptors&gt;</code> 라는 큰 태그 안으로 묶인다. 이를 보면 <code>&lt;interceptor&gt;</code> 태그로 여러 개의 인터셉터를 추가할 수 있다는 걸 알 수 있다.</p>
<p>그리고 <code>&lt;mapping path=“/**”&gt;</code> 태그를 통해 어떤 요청으로 들어올 경우, 인터셉터를 거치게 할지에 대해서 설정해준다. 물론 로그인 상태를 알아보기 위해서 사용하는 것이니 모든 경우를 대상으로 해주면 된다.</p>
<p>마지막으로 내부에 우리가 만든 LoginValidator 클래스를 생성해준다.</p>
<hr>
<h3 id="4-검사-대상-설정-및-검사-제외-대상-설정">4) 검사 대상 설정 및 검사 제외 대상 설정</h3>
<p>이렇게 하면 다 된 것 같지만, 막상 돌리면 “리디렉션한 횟수가 너무 많습니다.” 라는 에러 메시지가 브라우저에 나올 것이다.</p>
<p>이는 쉽게 설명하면 모든 요청에 대해서 우리가 설정해놨기 때문에 모든 요청이 인터셉터를 거치기 때문이다. 즉, 로그인 요청마저 인터셉터를 거쳐 에러가 나고, 재요청인 에러 페이지도 로그인 정보가 없기에 다시 반려된다.</p>
<p>이게 무한 반복되니, 당연히 저 문구가 뜨는 것도 당연하다. 그래서 검사 제외 대상을 설정해줘야 한다.</p>
<pre><code class="language-xml">&lt;interceptors&gt;
    &lt;interceptor&gt;
        &lt;mapping path=&quot;/**&quot;/&gt; 
        &lt;!-- 검사 제외 대상 --&gt;
        &lt;exclude-mapping path=&quot;/&quot;/&gt;
        &lt;exclude-mapping path=&quot;/error&quot;/&gt;
        &lt;exclude-mapping path=&quot;/member/loginProc&quot;/&gt;
        &lt;exclude-mapping path=&quot;/withoutLogin&quot;/&gt;

        &lt;beans:bean class=&quot;kh.spring.interceptor.LoginValidator&quot;/&gt;

    &lt;/interceptor&gt;
&lt;/interceptors&gt;</code></pre>
<p>Servlet-context.xml에 우리가 세팅한 인터셉터 태그 내부에 <code>&lt;exclude-mapping path=&quot;&quot;/&gt;</code> 로 검사를 제외할 요청을 적어주면 쉽게 해결된다.</p>
<hr>
<blockquote>
<p>드디어 수업은 끝! 이제는 파이널이다!</p>
</blockquote>
<p><strong>도움이 되셨다면, 좋아요. 댓글 남겨주세요 :)</strong></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Day 88. Web Socket 2 : 채팅 내역 유지하기]]></title>
            <link>https://velog.io/@ho_c/%EA%B5%AD%EB%B9%84%EA%B5%90%EC%9C%A1-Day-88</link>
            <guid>https://velog.io/@ho_c/%EA%B5%AD%EB%B9%84%EA%B5%90%EC%9C%A1-Day-88</guid>
            <pubDate>Sun, 26 Jun 2022 10:45:12 GMT</pubDate>
            <description><![CDATA[<p>오늘은 지난 시간에 만든 채팅 프로그램을 좀 더 발전시켜, 대화 목록을 DB에 저장하거나 채팅화면이 아닐지라도 채팅 목록을 수신받을 수 있도록 만들 예정이다. </p>
<p>그 전에! 지난 시간의 수업 내용을 전반적으로 살펴보자.</p>
<h1 id="지난-시간-정리">지난 시간 정리</h1>
<h2 id="http-https">HTTP, HTTPS</h2>
<p>웹 애플리케이션을 개발하면 어쩔 수없이 ‘HTTP, HTTPS’라는 프로토콜, 곧 통신 규칙을 사용한다. 근데 이 규칙에는 크게 두가지 특성이 있다.</p>
<h3 id="1-stateless">1) Stateless</h3>
<p>일단 상태 정보를 유지하지 않는다. 그래서 로그인 정보를 기억하지 않기 때문에 Session, cookie를 이용하여, 상태정보를 저장한다.</p>
<h3 id="2-request-response">2) Request, Response</h3>
<p>통신 구조가 “요청이 있어야만 응답이 있다”라는 규칙을 따른다. 그래서 요청 없으면 서버는 능동적으로 응답으로 데이터를 전송할 수 없다.</p>
<blockquote>
<p>이로 인해 채팅 애플리케이션은 Ajax를 통해서 구현하면 주기적으로 요청해 응답을 받지 않으면 구현이 어렵다. 하지만 안되는 걸 하는 만큼 서버 부하가 심하다. 이를 극복하는 방법이 Web Socket이다.</p>
</blockquote>
<h2 id="ws--wss">WS / WSS</h2>
<p>우리의 서버인 Tomcat은 HTTP, WS 모두 지원한다. 그래서 프로토콜에 따라 HTTP는 DS로 WS-Endpoint로 연결이 된다,</p>
<hr>
<h2 id="웹-소켓-프로세스">웹 소켓 프로세스</h2>
<h3 id="1-handshaking---ws과-톰캣-사이에서-일어나는-과정">1) HandShaking - WS과 톰캣 사이에서 일어나는 과정</h3>
<p><code>let ws = new WebSocket(URL)</code></p>
<p>우리가 프론트에서 해당 코드를 통해 웹 소켓 객체를 만들어 연결을 시도하면 1차적으로 핸드쉐이킹 과정이 발생한다. 즉, TOMCAT과 WS 사이에 통신 규정을 정하는데 이를 ‘HandShake’(OSI 7 Layer)라고 한다. (*규정을 정함) </p>
<p>따라서 통신-연결이 아니라, 통신이 연결되면서 연결 신호, 데이터는 어떤 형태 등등 뭘 보낼지, 어떻게 보내고, 어떻게 응답할지 이런 과정을 내부적으로 신호를 주고받는다.</p>
<h3 id="2-modify-handshake">2) Modify HandShake</h3>
<p>기본적으로 세팅된 HandShake(규칙)을 사용하면, HTTP로 오는 데이터 정보를 WS의 EndPoint로 끌어올 수 없다. 그래서 규칙을 바꿔서 HTTP Session을 넣어서 EndPoint로 보내줘야 한다.</p>
<p>EndPoint로 데이터를 보낼 땐, 톰캣에서 ServerEndpointConfig 객체에 넣어서 보낸다. 여기에는 설정정보 뿐만 아니라, 유저 속성을 넣을 수 있는 저장소가 존재한다. 그리고 저장소 안의 <code>.getUserProperties();</code> 칸에 HTTP Session을 넣어서 보낸다. </p>
<h3 id="3-endpointserverendpoint">3) EndPoint(@ServerEndPoint)</h3>
<p>핸드쉐이킹 과정에서 EndPoint 어노테이션을 따라, 요청된 엔드포인트를 찾고 인스턴스를 생성한다. 해당 인스턴스 안에서 각 연결에 따른 작업이 실행되고, 개별 세션을 갖고 있다.</p>
<h3 id="4-onopen">4) @OnOpen</h3>
<p>핸드쉐이킹이 끝나고 WS 세션이 연결되었을 때, 실행되는 메서드다. 주로 채팅에서는 각 인스턴스의 session 정보를 Set에 저장한다.</p>
<pre><code class="language-java">clients.add(session0); // HashSet에 동일 인스턴스들의 세션 정보를 넣어둔다.</code></pre>
<h3 id="5-ws-session">5) WS Session</h3>
<pre><code class="language-java">private static Set&lt;Session&gt; clients = Collections.synchronizedSet(new HashSet&lt;&gt;());</code></pre>
<p>모든 WS 연결자들의 세션을 공통으로 관리할 수 있도록, 쉽게 말하면 각 인스턴스가 메시지를 연결자 모두에게 보낼 수 있도록 ‘정적 변수’를 만들어 준다. </p>
<p>제대로 들어가면 Set 보다는 Map을 사용해 더 다양한 정보를 담아둔다. </p>
<h3 id="6-onmessage">6) @OnMessage</h3>
<p>클라이언트에서 WS 객체를 통해 서버로 메시지를 보내면, @OnMessage 어노테이션으로 연결된 메서드에서 매개변수로 데이터를 받는다. 이렇게 받은 데이터를 연결된 모든 사람에게 보낸다.</p>
<p>주로 이 상황에서도 멀티쓰레드에 의한 동시성 문제로 인해 세션을 저장한 Set의 길이가 줄어들면서 예외가 발생한다. 그래서 동기화 블록으로 작업을 고정처리 해준다.</p>
<pre><code class="language-java">synchronized(clients) { // 동기화 블록으로 고정처리 해준다.
    for(Session client : clients) {
        try{
            clients.getBasicRemote().sendText(message);
        } catch(Exception e) {}
    }
}</code></pre>
<h3 id="7-onclose-onerror">7) @OnClose, @OnError</h3>
<p>해당 어노테이션으로 세션이 닫히고, 에러가 생겼을 때 세션 저장소에서 각 세션을 지운다.</p>
<h3 id="8-연결이-끊김">8) 연결이 끊김</h3>
<p>결과적으로 우리가 방을 나가고, 다시 들어가면 기존 웹 소켓이 사라지고, 새로운 웹 소켓이 생성된다. 즉, 페이지 전환마다 웹 소켓은 새로 만들어진다. 그래서 처음 대화 내용은 다 사라진다.</p>
<blockquote>
<p>물론 그 원인은 DB의 영역이 아닌 HTML로만 남겨놔서 그런 부분도 있다. 이제부터는 이전 대화 내용, 연결이 끊겨도 채팅을 유지하는 방법을 찾아보자.</p>
</blockquote>
<hr>
<h1 id="채팅-내역-유지-구현">채팅 내역 유지 구현</h1>
<p>먼저 대화 내용은 그 어디에도 저장되지 않고 HTML 안에 흔적으로 남을 뿐이다. 이에 대한 정책적인 부분은 개발자를 따른다. 하지만 우리는 유지하고 싶으니까, 그 방법을 알아보자.</p>
<p><strong>일단 크게 2가지 방향성이 있다.</strong></p>
<ul>
<li>로그아웃 전까지만 유지</li>
<li>반영구적으로 유지 </li>
</ul>
<h2 id="1-로그아웃-전까지만-유지">1. 로그아웃 전까지만 유지</h2>
<p>로그아웃 전까지만 유지한다는 것은 말 그대로 로그인 된 상태에선 페이지 전환이 일어나도, 채팅은 지속으로 진행된다는 것이다. 예를 들어 우리가 채팅 페이지에서 내 정보 확인 페이지를 다녀와도, 그 사이에 진행된 채팅 내역을 볼 수 있다는 것이다.</p>
<p>이 방법이 가장 간단한 방법인데, 주로 FIFO(First In First Out) 구조인 큐(Queue)를 사용해서 각 세션에서 보내는 메시지들을 저장하는 것이다. 그럼 FIFO(First In First Out)이 뭘까?</p>
<p>데이터를 다루는 자료구조 중 하나인데, 리스트의 형태를 갖고 있다. 다만 데이터가 들어오는 입구와 나가는 곳이 있는데, 일정 길이 안에서 데이터가 차면 먼저 들어온 데이터부터 나가는 형식이다.</p>
<p>이를 구현하기 위해서 구글에서 만든 EvictingQueue를 사용할 것이다. 그럼 차근 차근 사용법을 알아보자.</p>
<hr>
<h4 id="guava-evictingqueue">Guava EvictingQueue</h4>
<p>대화 내역을 EvictingQueue을 저장한 다음, 이를 세션에 저장하여 RAM에다가 넣어두면, 큐가 꽉 찼다는 가정하에 오래 순서대로 자동 삭제가 된다. </p>
<h3 id="1-guava-라이브러리-저장">1) Guava 라이브러리 저장</h3>
<p>먼저 Maven에서 Guava 라이브러리를 끌어와 pom.xml에 넣어준다.</p>
<pre><code class="language-xml">&lt;dependency&gt;
    &lt;groupId&gt;com.google.guava&lt;/groupId&gt;
    &lt;artifactId&gt;guava&lt;/artifactId&gt;
    &lt;version&gt;31.1-jre&lt;/version&gt;
&lt;/dependency&gt;</code></pre>
<p>EvictingQueue는 Guava 라이브러리 내부에 있기에, 톰캣이 실행되면서 메모리 상에 인터페이스가 존재하니, 이를 가져다 쓰면 된다.</p>
<hr>
<h3 id="2-인스턴스-생성">2) 인스턴스 생성</h3>
<p>생성 방법은 간단하다. EvictingQueue의 클래스 메서드를 통해서 인자로 길이를 저장해주면 된다.</p>
<pre><code class="language-java">EvictingQueue&lt;String&gt; queue = EvctiongQueue.create(2); // 최대 2개 저장
queue.add(“1”);
queue.add(“2”);
queue.add(“3”);</code></pre>
<p>queue를 출력하면 {2, 3}이 출력된다.</p>
<hr>
<h3 id="3-클래스-변수로-만들기">3) 클래스 변수로 만들기</h3>
<p>이렇게 생성된 queue는 클래스 변수가 되어야 한다. 이건 앞서 WS 세션을 저장한 Set과 마찬가지인데, 나중에 저장된 대화 내역을 클라이언트로 뿌려줄 것이기 때문에 모두가 하나의 객체를 공유해야 한다.</p>
<pre><code class="language-java">private static EvictingQueue&lt;ChatDTO&gt; queue = EvictingQueue.create(20);</code></pre>
<p>참고로 제너릭은 <code>ChatDTO</code>으로 정하여, 이 DTO 안에 각 세션에서 보낸 대화와 정보를 저장해둘 것이다. </p>
<p><em><strong>[ ChatDTO ]</strong></em></p>
<pre><code class="language-java">public class ChatDTO {
    private String sender;
    private String msg;
    private Timestamp msg_Date;

    public ChatDTO() {}

    public ChatDTO(String sender, String msg, Timestamp msg_Date) {
        super();
        this.sender = sender;
        this.msg = msg;
        this.msg_Date = msg_Date;
    }

    public String getSender() {
        return sender;
    }

    public void setSender(String sender) {
        this.sender = sender;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public Timestamp getMsg_Date() {
        return msg_Date;
    }

    public void setMsg_Date(Timestamp msg_Date) {
        this.msg_Date = msg_Date;
    }
}</code></pre>
<h3 id="4-onmessage">4) @onMessage</h3>
<p>이제 메시지를 보낼 때마다 큐 안에 데이터를 저장해둘 것이다.</p>
<pre><code class="language-java">@OnMessage
public void onMessage(String message) {
    synchronized (clients) {

        String longinID = (String)httpSession.getAttribute(&quot;loginID&quot;);
        queue.add(new ChatDTO(longinID, message, new Timestamp(System.currentTimeMillis()))); // 메시지를 보내면 그 정보를 DTO에 담는다.
    }    
}</code></pre>
<h3 id="5-onopen">5) @onOpen</h3>
<p>@onMessage에서 저장한 대화 내역을 이제, WS로 접속하는 시점에 클라이언트로 보내서 이를 HTML로 출력하게 할 것이다. 단, 형식은 사용하기 쉽도록 JSON으로 보낸다.</p>
<pre><code class="language-java">@OnOpen
public void onOpen(Session session, EndpointConfig config) {
    this.httpSession = (HttpSession)config.getUserProperties().get(&quot;hSession&quot;);
    clients.add(session);

    try {
        session.getBasicRemote().sendText(gson.toJson(queue)); // 접속하면 queue에 들은 내용을 프론트로 보낸다.
    } catch (IOException e) {
        e.printStackTrace();
    }    
}</code></pre>
<h3 id="6-클라이언트에서-출력하기">6) 클라이언트에서 출력하기</h3>
<p>돌아온 JSON을 반복문을 돌려서 출력해준다.</p>
<pre><code class="language-html">ws.onmessage  = function(e){

    let data = JSON.parse(e.data)

    for (let i = 0; i&lt;data.length; i++){

        if(&#39;${loginID}&#39;== data[i].sender){

            let box = $(&quot;&lt;div style=&#39;text-align:right;&#39;&gt;&quot;)

            let id = $(&quot;&lt;div class=&#39;id&#39;&gt;&quot;);
            let msg = $(&quot;&lt;span class=&#39;msg&#39; style=&#39;background-color:yellow;&#39;&gt;&quot;);
            let time = $(&quot;&lt;div class=&#39;time&#39;&gt;&quot;)
            let line = $(&quot;&lt;br&gt;&quot;);

            id.append(data[i].sender);
            msg.append(data[i].msg);
            time.append(data[i].time)

            box.append(id);
            box.append(msg);
            box.append(time);

            $(&quot;#msg_box&quot;).append(box);
            $(&quot;#msg_box&quot;).append(line);

        } else{

            let box = $(&quot;&lt;div&gt;&quot;)

            let id = $(&quot;&lt;div class=&#39;id&#39;&gt;&quot;);
            let msg = $(&quot;&lt;span class=&#39;msg&#39;&gt;&quot;);
            let time = $(&quot;&lt;div class=&#39;time&#39;&gt;&quot;)
            let line = $(&quot;&lt;br&gt;&quot;);

            id.append(data[i].sender);
            msg.append(data[i].msg);
            time.append(data[i].time)

            box.append(id);
            box.append(msg);
            box.append(time);

            $(&quot;#msg_box&quot;).append(box);
            $(&quot;#msg_box&quot;).append(line);

        }                
    }            
}</code></pre>
<hr>
<h2 id="2-db에-저장하기">2. DB에 저장하기</h2>
<h3 id="1-개요">1) 개요</h3>
<p>이 방식은 지우지 않는 한 대화 내용이 사라지지 않는다. 이를 메신저형라고 하는데, 이를 위해선 DB에다가 채팅 내용을 전부 기록한다.</p>
<blockquote>
<p><strong>DB 저장 방식은 다음 2가지로 간단히 볼 수 있다.</strong></p>
<blockquote>
<ul>
<li>입력이 올 때마다 DB에 넣는다.</li>
</ul>
</blockquote>
</blockquote>
<ul>
<li>쌓아놓고 DB에 넣는다.</li>
</ul>
<p>데이터를 넣는 과정는 @OnMessage 안에서 데이터를 사용자에게 뿌리기 전에 진행되며, 이 때문에 DB와 연결 과정을 Service 페이지로 사용하는게 좋다.</p>
<p>그래서 흔히들 @Autowired하거나 @Component로 Service 계층을 만들어서 해서 사용하려 한다. 물론 만들 수는 있고, 적용도 된다. </p>
<blockquote>
<p>문제는 사용자는 매번 소켓을 통해 EndPoint를 새로 만들어서 쓰기 때문에 전혀 상관이 없다. (이게 기본 매커니즘이다)</p>
</blockquote>
<p>더욱이 스프링 어노테이션으로 설정 인스턴스는 그 당시, 스프링 풀이 실행되면서 딱 한 번만 가능하다. 그래서 DI로 미리 꽂아놓는 것도 의미가 없다. </p>
<p>그래서 DI가 아닌 DL로 내가 원할 때 메모리에 생성된 인스턴스를 찾아서 꺼내와서 사용한다. 즉, 스프링 요소가 아닌 클래스가 스프링 풀 안의 것을 가져다 쓰고 싶으면 DL을 하면 된다는 것이다. </p>
<blockquote>
<p>전체적으로 다시 보면 Spring Element로 웹 소켓 EndPoint 인스턴스를 만들고, 쓸 수도 있고, 근데 아무리 DI해봤자, 사용자가 웹 소켓을 사용할 때 기본 매커니즘은 스프링 풀 밖에다가 매번 새로 만들어, 아예 생성 시점도 맞지도 않다. 따라서 무용지물이 된다.</p>
</blockquote>
<p>결과적으로 DI로 서비스 레이어를 DI 해봤자 의미가 없게 된다. 따라서 스프링 풀 밖에서 스프링 풀 안에 있는 요소를 DL로 꺼내 쓰면 된다.</p>
<hr>
<h3 id="2-문제">2) 문제</h3>
<p>일단, Spring Container라고 불리는 것은 코드 상에선 <code>ApplicationContext</code> 클래스를 의미한다. 그래서 스프링에서 DL을 할 때는, 해당 참조변수에 xml를 분석해서 컨테이너를 올려 놓고 <code>getBean(“클래스명”)</code> 으로 가져다 쓴다.</p>
<p>근데 DL을 할 건데, 중요한 건 EndPoint의 우리는 톰캣이 만들어 준 스프링 컨테이너의 주소를 모른다는 것이다.</p>
<h3 id="3-implement-applicationcontextaware">3) implement ApplicationContextAware</h3>
<p>앞서 문제는 <code>getBean();</code>이 들어있는 인스턴스의 주소를 모른다는 것이다. 그래서 해결 방법은 직접 <code>ApplicationContextProvider</code> 클래스를 만들어 사용한다.</p>
<p>먼저 <code>ApplicationContextProvider</code>라는 이름의 클래스를 하나 만들어 준 뒤, 인터페이스로 <code>ApplicationContextAware</code>를 상속해준다.</p>
<pre><code class="language-java">@Component
public class CTXProvider implements ApplicationContextAware{    
}</code></pre>
<h3 id="4-오버라이딩">4) 오버라이딩</h3>
<p>인터페이스를 상속받은 클래스는 그 내용을 오버라이딩해줘야 한다.  <code>setApplicationContext()</code> 메서드를 오버라이딩 할 것이다.</p>
<pre><code class="language-java">@Component
public class CTXProvider implements ApplicationContextAware(자각하다){

    public static ApplicationContext ctx;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {    
        this.ctx = applicationContext;
    }    
}</code></pre>
<p>보다시피 setApplicationContext는 매개변수로 ApplicationContext, 곧 스프링 컨테이너의 주소를 받는다. 그래서 우리는 이를 사용할 수 있는데, 인스턴스 해당 인스턴스 자체는 서버 구동 시, 만들어지니 이를 클래스 변수로 연결해서 사용하면 된다. (어차피 한 개만 만들어지고...)</p>
<p>이 과정을 쉽게 보면, 웹 애플리케이션 같은 스프링 컨테이너의 주소를 알 수 없는 상황에선 DL을 할 수 없으니까, 대안으로 아무 클래스를 만들고 ApplicationContextAware를 상속 받으라는 것이다.</p>
<p>그렇게 <strong>ApplicationContextAware</strong>를 구현하는 클래스가 Bean으로 생성되었다면, 그 클래스는 <strong>setApplicationContext()</strong> 추상메서드를 채웠을테니, 스프링이 스프링 컨테이너가 가동되는 시점에, 이 주소를 메서드의 매개변수로 넣어준다는 것이다.</p>
<h3 id="5-service-레이어-생성">5) Service 레이어 생성</h3>
<p>실험용으로 Service 레이어를 만들어준다. 이때 어노테이션 처리를 해줬기 때문에 해당 인스턴스도 메모리 상에 존재하게 된다.</p>
<pre><code class="language-java">@Service
public class ChatService {

    public void test() {
        System.out.println(&quot;서비스 동작 확인&quot;);
    }
}</code></pre>
<h3 id="6-getbean자료형class">6) .getBean(자료형.class)</h3>
<p>이제 엔드포인트에서 <code>.getBean()</code>을 통해서 가져다 사용하면 되는데, 글이 길어지니 DAO 로직을 개인 재량에 맡기도록 하겠다.</p>
<p>[ EndPoint ]</p>
<pre><code class="language-java">@ServerEndpoint(value = &quot;/chat&quot;, configurator = ChatConfigurator.class)
public class ChatEndpoint {

    private ChatService cServ = CTXProvider.ctx.getBean(ChatService.class)

    @OnMessage
    public void onMessage(String message) {
        cServ.insert(message);
    }
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[Day 87. Web Socket 1 : 채팅 프로그램 구현]]></title>
            <link>https://velog.io/@ho_c/%EA%B5%AD%EB%B9%84%EA%B5%90%EC%9C%A1-Day-87</link>
            <guid>https://velog.io/@ho_c/%EA%B5%AD%EB%B9%84%EA%B5%90%EC%9C%A1-Day-87</guid>
            <pubDate>Sun, 26 Jun 2022 10:36:47 GMT</pubDate>
            <description><![CDATA[<p>어느덧 국비 과정의 마지막 수업이 다가오고 있다. 시간이 이렇게 빠르다니. 오늘은 웹 소켓을 이용한 기본적인 채팅 프로그램을 만들어 보자.</p>
<h2 id="채팅-프로그램">채팅 프로그램</h2>
<h2 id="1-개요">1. 개요</h2>
<p>채팅 프로그램엔 여러 유형이 있고, 그에 따라 난이도가 천차만별이다. 하지만 공통점은 클라이언트-서버가 채팅하는 게 아닌, 클라이언트 간의 채팅을 서버가 중개해주는 것이다.</p>
<p>근데 <strong>“요청이 있어야 응답이 있다”</strong>라는 HTTP 기반 웹 애플리케이션에서는 가능은 하지만 매우 어려운 얘기이다. 쉽게 말하면 채팅을 친 클라이언트와 서버 간에는 가능하지만, 그 채팅을 받아야 할 다른 클라이언트들은 요청하지 않았기에 응답을 받지 못한다.</p>
<p>이를 HTTP로 구현하려면 Ajax로 주기적으로 요청을 보내 서버에 갱신된 내용을 받아오는 법밖에 없다. 하지만 이 경우 서버가 주기적(n초)으로, 서버 접속자 수(n명)만큼, n^2의 요청을 받아 큰 서버 부하가 발생한다.</p>
<p>반대로 부하를 줄이려면, 주기적으로 요청을 보내는 시간을 늘리면 트래픽이 줄어들지만, 이 때문에 채팅의 <strong>‘반응성’</strong>을 잃어버리게 된다. 이런 이유로 AJAX로 채팅을 만드는 것은 적합하지 않다.</p>
<blockquote>
<p>이 문제를 해결하기 위해, HTTP를 사용하지 않고 Web Socket을 만들어 사용한다.</p>
</blockquote>
<hr>
<h2 id="2-websocket">2. WebSocket</h2>
<blockquote>
<p>그럼 WebSocket은 무엇일까?</p>
</blockquote>
<p>먼저 파일 전송 시스템에서 사용한 소켓은 ‘양방향 스트림’이 가능했다. 즉, 보내고 받는 것이 가능했다. 그리고 이 개념을 웹에 차용한 게 WebSocket이다.</p>
<blockquote>
<p>여기서 WebSocket은 그 자체가 Http처럼 하나의 프로토콜이라 http와 호환이 되지 않아, http session의 저장된 로그인 정보를 확인할 수 없는 문제가 있는데, 이는 차차 해결해보자.</p>
</blockquote>
<h2 id="3-채팅-프로그램-구현">3. 채팅 프로그램 구현</h2>
<h3 id="0-웹-소켓-라이브러리-다운로드">0) 웹 소켓 라이브러리 다운로드</h3>
<p>웹 소켓을 사용하려면 Maven에서 웹 소켓 라이브러리를 다운 받아야한다. 이 웹소켓 라이브러리는 지금 단계에선 스프링과 연계되어 사용되지 않는다,</p>
<pre><code class="language-xml">&lt;dependency&gt;
    &lt;groupId&gt;javax.websocket&lt;/groupId&gt;
    &lt;artifactId&gt;javax.websocket-api&lt;/artifactId&gt;
    &lt;version&gt;1.1&lt;/version&gt;
    &lt;scope&gt;provided&lt;/scope&gt;
&lt;/dependency&gt;</code></pre>
<h3 id="1-client">1) Client</h3>
<p>일단 기본적인 UI들을 만들어 주고, 시작은 클라이언트에서 JS로 웹소켓 통신을 요청하면서 시작된다.</p>
<pre><code class="language-html">&lt;script&gt;
    $(function(){
        let ws = new WebSocket(&quot;url&quot;); . 

        $(&quot;#input&quot;).on(&quot;keydown&quot;, function(e){
            if(e.keyCode == 13){
                let text = $(&quot;#input&quot;).html(); 
                let line = $(&quot;&lt;div&gt;&quot;); 
                line.append(text); 

                $(&quot;#input&quot;).html(&quot;&quot;); 
                ws.send(text);

                return false; 
            }
        });

        ws.onmessage  = function(e){
            let line = $(&quot;&lt;div&gt;&quot;);
            line.append(e.data);
            $(&quot;#msg_box&quot;).append(line);
        }

    })
&lt;/script&gt;</code></pre>
<ul>
<li>일차적으로 페이지가 로딩되면서 웹소켓 객체를 생성하여 인자로 URL을 준다.</li>
<li>그래서 페이지 로딩 시, 서버의 엔드포인트와 웹 소켓 통신이 연결된다.</li>
<li>웹소켓 객체(ws)의 .send함수를 통해서 엔드포인트로 입력한 텍스트를 전송한다.</li>
<li>.onmessage함수는 엔드포인트로부터 오는 응답을 받아서 처리한다.</li>
</ul>
<hr>
<h3 id="2-server--endpoint-생성">2) Server : endpoint 생성</h3>
<p>요청이 들어오는 서버 클래스를 Endpoint라고 한다. 일차적으로 이 클래스들을 관리할 패키지와 각 요청에 맞는 클래스들을 만들어 주는데, 우리가 구현할 기능은 채팅 하나니까, <code>ChatEndPoint</code>만 만들면 된다.</p>
<p><em><strong>첫 번째 포인트는</strong></em> WS로 들어온 요청은 스프링의 DS로 가지 않는다. 왜냐면 DS는 HTTP기반 요청을 받기 때문이고, 웹 소켓 자체는 스프링하고 연관이 없기 때문이다. </p>
<p>그래서 톰캣 서버가 구동되면 HTTP 요청을 받을 DS와 WS 요청을 받은 Endpoint가 메모리 안에 생성된다.</p>
<blockquote>
<p><strong>그럼 어떻게 EndPoint인지 알까?</strong> 이 역시 어노테이션으로 구분해준다.</p>
</blockquote>
<pre><code class="language-java">@ServerEndpoint(“/chat&quot;) // 이 경로로 요청이 들어온다.
public class ChatEndpoint {}

// 이런 식으로 해당 엔드포인트가 따를 규칙을 설정할 수 있다
@ServerEndpoint(value = &quot;/chat&quot;, configurator = WebSocketConfigurator.class) 
public class ChatEndpoint {}</code></pre>
<br>

<p>_*<em>두 번째 포인트는 *</em>_이런 엔드포인트 인스턴스는 접속자 한 명당 같은 종류의 인스턴스가 생성된다는 것이다. 그래서 결합성을 따지는 Spring 측면의 접근이 의미없다.</p>
<p>이렇게 만들어진 엔드포인트들은 내부의 어노테이션으로 각 작업을 할당해놓는다. 이제부터는 각 어노테이션의 할당된 역할을 알아보자.</p>
<hr>
<h3 id="3-onopen">3) @onOpen</h3>
<ul>
<li>클라이언트의 요청으로 클라와 서버를 연결한다.</li>
<li>Session 인스턴스로 연결 정보를 불러올 수 있다.</li>
</ul>
<p>클라에서 요청이 들어와 연결이 만들어 질 때, 실행되는 어노테이션이다. 해당 메서드들로 접속 시, 이전 대화목록을 불러올 수도 있다.</p>
<pre><code class="language-java">@OnOpen 
public void onConnect(Session session, EndpointConfig config) { 
    this.hSession = (HttpSession)config.getUserProperties().get(&quot;hSession&quot;); 
    clients.add(session);
    System.out.println(hSession.getAttribute(&quot;loginID&quot;));
}</code></pre>
<p>기본적으로 웹 소켓 세션을 가져올 수 있다. 이건 HTTP 세션과 다른 세션이며, 클라이언트 하나당 한 개씩 존재한다. 위 코드는 hSession은 HTTP 세션인데, 이건 뒤쪽에서 설명하겠다.</p>
<hr>
<h3 id="4-onmessage">4) @onMessage</h3>
<ul>
<li>클라이언트에서 메시지를 보내면 동작하는 메서드를 나타내는 어노테이션이다.</li>
<li>인자값으로 클라에서 보낸 메시지를 받을 수 있다.</li>
<li>주로 로직은 연결된 클라이언트 세션들에게 받은 메시지를 전송한다.</li>
</ul>
<p>클라이언트의 .send 메서드가 실행되면, 입력된 내용이 서버로 전달된다. 그러면 서버에선 @onMessage 어노테이션으로 정의된 메서드로 그 내용을 처리한다.</p>
<pre><code class="language-java">@OnMessage 
public void onMessage(String message) {

    synchronized (clients) {
        for(Session client : clients) { .
            try {
                client.getBasicRemote().sendText(message);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}</code></pre>
<p>위에는 동기화 처리가 되어 있긴 한데, 그건 잠깐 넘어가자. 여기서 중요한 것은 받은 메시지를 처리하는 로직이다. 다시 말하면 서버로 보낸 메시지를 채팅 참여자들이 볼 수 있도록 서버가 전달해줘야 한다는 것이다.</p>
<p>일단 메시지 자체는 <code>String messge</code> 매개변수로 받아줄 수 있다. 근데 문제는 각자 다른 엔드포인트 인스턴스이기 때문에 이를 연결해줄 수 없다는 것이다.  따라서 우리가 첫째로 할 것은 같은 종류의 엔드 포인트들이 자신들의 위치를 공유할 수 있는 저장소를 만들어주는 것이다.</p>
<h4 id="1-클라이언트-ws세션-정보-담기">(1) 클라이언트 WS세션 정보 담기</h4>
<p><em><strong>① private static Set<Session> clients</strong></em></p>
<p>세션에는 각 클라이언트의 정보가 담겨있다. 그래서 이것만 알면 우리는 세션을 통해서 서로 연락을 주고받을 수 있다.  일단 참조 변수를 적어주긴 했는데, 중요한 건 그 변수가 Set 컬렉션의 static, 그리고 private라는 것이다.</p>
<p>일단 Set 컬렉션은 리스트와 비슷하지만, 중복되는 값을 저장할 수 없어서 사용한다. 이 자료형 안에 각 세션 정보를 저장해두는 것이다. </p>
<p>그리고 이를 static으로 해서 별다른 인스턴스 생성 없이 항상 존재하게 만든다. 또한 static의 특성상 모든 인스턴스는 하나의 클래스 변수를 공유하게 한다. 마지막으로 private를 통해서 같은 종류의 인스턴스들 끼리만 공유하도록 한다.</p>
<pre><code class="language-java">private static Set&lt;Session&gt; clients = new HashSet&lt;&gt;();</code></pre>
<p><em><strong>② @onOpen</strong></em></p>
<p>여기서 @onOpen이 다시 재등장한다. 일단 메모리의 영역에서만 본다면, 연결되면서 내가 해당 채팅에 참여한다는 행위로 보는 게 좋을 것 같다.</p>
<pre><code class="language-java">clients.add(session);</code></pre>
<hr>
<h4 id="2-받은-메시지를-채팅-참여자들에게-전달하기">(2) 받은 메시지를 채팅 참여자들에게 전달하기</h4>
<p>자 이제 메시지를 받을 참여자들의 세션을 한 군데 취합하였다. 그래서 다음 단계는 그 저장소에 적힌 연락처들을 하나씩 보내서 메시지를 보내면 된다. 이게 바로 @onMessage의 주 역할이다.</p>
<pre><code class="language-java">for(Session client : clients) {  // 연락처를 한 개씩 꺼내온다.
    try {
        client.getBasicRemote().sendText(message); // 메시지를 보내는 메서드
    } catch (Exception e) {
        e.printStackTrace();
    }
}</code></pre>
<hr>
<h4 id="3-안정화-작업">(3) 안정화 작업</h4>
<p>앞선 두 단계까지만 하면 충분할 것 같지만, 문제는 저 상태에서 누군가 채팅방을 나가거나, 브라우저를 닫아버리면 clients set의 길이가 갑자기 줄어들면서 에러가 발생한다.</p>
<p>이는 자바의 멀티쓰레드에 의한 문제인데, 여기서 쓰레드는 일하는 공간? 정도로 생각하는게 편하겠다. 무튼 채팅을 보내고, 클라이언트가 나가는 상황이 동시에 일어날 수 있다는 것이다. 왜? 작업 공간이 여러개라서</p>
<p>이런 문제를 극복하기 위해서 ‘안정화 작업’이 필요하다.</p>
<p><em><strong>① 동기화 Set 생성</strong></em></p>
<p>세션 정보를 저장할 Set 자체를 동기화 상태로 만들어 생성한다. 그렇게 되면 이 Set의 작업 중일 때는 다른 작업은 못하게 된다.</p>
<pre><code class="language-java">private static Set&lt;Session&gt; clients = Collections.synchronizedSet(new HashSet&lt;&gt;());</code></pre>
<p><em><strong>② 동기화 블록으로 감싸기</strong></em></p>
<p>그렇지만 메시지를 보내는 행위 자체는 막을 수 없다. 그래서 반복문으로 메시지를 보내는 작업도 동기화 블록으로 감싸서, 해당 작업이 끝날 때까지 다른 작업이 진행되지 못하게 한다.</p>
<pre><code class="language-java">// 동기화 블럭 : 인자에 대한 작업이 끝나기 전까지 그 다음 작업은 대기 상태가 된다.
synchronized (clients) {
    for(Session client : clients) { .
        try {
            client.getBasicRemote().sendText(message); 
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}</code></pre>
<hr>
<h3 id="4-onclose--onerror">4) @OnClose / @OnError</h3>
<p>근데 안정화 문제보다 앞서는 것은 현 상태에서 브라우저를 꺼버리게 되면 클라이언트의 WS의 세션은 사라지는데, 서버의 세션은 사라지지 않고 저장소에 남아있게 된다는 것이다.</p>
<p>그래서 두 세션의 차이로 인해서 메시지를 보내게 되면 이미 클라이언트에선 사라진 세션에 메시지를 보내려다 보니 에러가 난다.</p>
<p>때문에 @OnClose / @OnError 어노테이션으로 클라이언트가 세션을 종료하거나, 에러가 날 경우 저장소에서 해당 세션을 제거해줘야 한다. ( 엔드포인트 하나당 세션 한 개라는 것을 기억하자 )</p>
<pre><code class="language-java">@OnClose // 연결을 클라이언트에서 끊었을 때 동작
public void onClose(Session session) {
    clients.remove(session); // 이 세션은 이 엔드포인트에서만 사용한다.
}

@OnError
public void onError(Session session, Throwable t) {
    t.printStackTrace();
    clients.remove(session);
}</code></pre>
<hr>
<h3 id="5-식별자-가져오기">5) 식별자 가져오기</h3>
<p>기본적인 채팅은 지금 다 가능하다. 이걸 좀 더 발전시키면, 메시지 전송자에 대한 정보를 알고 싶다. 근데 문제는 웹소켓 세션은 HTTP Session하고 달라서, 사용자 정보가 안 담긴다.</p>
<p>그러니까 HTTP에서 하던 <code>(String)session.getAttribute(&quot;loginID&quot;);</code> 이런게 안된다는 것이다.
따라서 이를 우리가 직접 구현 해줘야 한다.</p>
<p>일단 세션 정보를 가져올 대상은 WS 세션이 아닌, HTTP 세션에서 가져온다. 그러기 위해선 WS 요청이 톰캣을 지나는 타이밍에 DS에서 사용되는 HTTP 세션 객체를 받은 다음에 EndPoint로 가야 한다.</p>
<h4 id="1-configurator-패키지-클래스-추가">(1) configurator 패키지, 클래스 추가</h4>
<p>이 설정을 위해선 먼저 패키지와 클래스를 추가해야 한다. 이걸 만든 이유는, 요청이 들어오면서 설정 정보를 톰캣의 Configurator 객체로 받기 때문인데, 우리는 이 객체를 상속받아서 커스텀 한 다음에 거기서 정보를 추출할 것이다. </p>
<h4 id="2-handshake--통신-전-통신-규약에-대한-필요한-정보를-주고받는-과정">(2) HandShake : 통신 전, 통신 규약에 대한 필요한 정보를 주고받는 과정</h4>
<p>네트워크 용어로 ‘프로토콜 성립 과정’을 의미한다. A라는 장치가 B라는 장치에 네트워크 통신을 하자고 요청하고, B는 그에 응답하는 과정을 말한다. </p>
<p>이 프로토콜 과정은 단순히, 요청-응답이 끝나는게 아니다. 앞으로의 A와 B의 통신 규약에 대해서 두 장치가 통신 정보를 주고받는 것이다. 이 주고받는 과정이 3번 정도기 때문에 Three way Handshake라 한다. </p>
<h4 id="3-상속-및-override">(3) 상속 및 Override</h4>
<p>우리가 상속 받을 톰캣의 Configurator에 이 HandShake의 통식 규칙이 담겨 있다. 결국 우리가 한다는 건 Handshake 과정을 오버라이딩하여 서로의 통신 규칙을 커스텀하는 것이다.</p>
<pre><code class="language-java">public class ChatConfigurator extends Configurator{

    @Override
    public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) { // 통신 규칙 정하기

        HttpSession session = (HttpSession)request.getHttpSession(); // 1) 세션 정보를 꺼내라

        sec.getUserProperties().put(&quot;hSession&quot;, session); // 2) 엔드포인트 전달하고 싶은 사용자 정보를 담음 ServerEndpointConfig는 엔드포인트에 접근 가능하다.

    }
}</code></pre>
<p>여기서 우리가 매개변수로 받는 <code>ServerEndpointConfig sec</code>를 엔드포인트에서 사용하는 <code>EndpointConfig</code>를 상속받으며, 엔드포인트의 어노테이션으로 해당 규칙을 따른다는 것을 명시해줘야 한다.</p>
<p><em><strong>[ ChatEndPoint ]</strong></em></p>
<pre><code class="language-java">@ServerEndpoint(value = &quot;/chat&quot;, configurator = ChatConfigurator.class)
public class ChatEndpoint {

}</code></pre>
<h4 id="4-endpoint-설정-및-httpsession-끌어오기">(4) Endpoint 설정 및 HttpSession 끌어오기</h4>
<pre><code class="language-java">private HttpSession hSession;


@OnOpen // 
public void onConnect(Session session, EndpointConfig config) { 
    this.hSession = (HttpSession)config.getUserProperties().get(&quot;hSession&quot;); // 넣어놨던 HTTP Session을 꺼낸다.
    clients.add(session);
    System.out.println(hSession.getAttribute(&quot;loginID&quot;)); // 세션 안의 키를 통해 값을 꺼낸다.
}</code></pre>
<hr>
<h4 id="6-내가-보낸-메시지-받은-메시지-분류">6) 내가 보낸 메시지, 받은 메시지 분류</h4>
<p>이렇게 받은 정보는 JSON으로 클라이언트로 보내준다. 나는 GSON 라이브러리를 사용해서 JSON으로 쉽게 변환한다. </p>
<pre><code class="language-java">@OnMessage
public void onMessage(String message) {
    synchronized (clients) {

        String longinID = (String)httpSession.getAttribute(&quot;loginID&quot;);

        try {
            for(Session client : clients) {
                String nowTime = getTime();

                JsonObject data = new JsonObject();
                data.addProperty(&quot;sender&quot;, longinID);
                data.addProperty(&quot;msg&quot;, message);
                data.addProperty(&quot;time&quot;, nowTime);

                JsonArray arr = new JsonArray();
                arr.add(data);

                client.getBasicRemote().sendText(arr.toString());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}</code></pre>
<p>클라이언트에선 JSP로 미리 로그인 ID를 세팅하여 다음과 같이 동적바인딩을 통해 구별해 출력한다.</p>
<pre><code class="language-javascript">ws.onmessage  = function(e){

    let data = JSON.parse(e.data)

    for (let i = 0; i&lt;data.length; i++){
        if(&#39;${loginID}&#39;== data[i].sender){

            let box = $(&quot;&lt;div style=&#39;text-align:right;&#39;&gt;&quot;)

            let id = $(&quot;&lt;div class=&#39;id&#39;&gt;&quot;);
            let msg = $(&quot;&lt;span class=&#39;msg&#39; style=&#39;background-color:yellow;&#39;&gt;&quot;);
            let time = $(&quot;&lt;div class=&#39;time&#39;&gt;&quot;)
            let line = $(&quot;&lt;br&gt;&quot;);

            id.append(data[i].sender);
            msg.append(data[i].msg);
            time.append(data[i].time)

            box.append(id);
            box.append(msg);
            box.append(time);

            $(&quot;#msg_box&quot;).append(box);
            $(&quot;#msg_box&quot;).append(line);

        } else{

            let box = $(&quot;&lt;div&gt;&quot;)

            let id = $(&quot;&lt;div class=&#39;id&#39;&gt;&quot;);
            let msg = $(&quot;&lt;span class=&#39;msg&#39;&gt;&quot;);
            let time = $(&quot;&lt;div class=&#39;time&#39;&gt;&quot;)
            let line = $(&quot;&lt;br&gt;&quot;);

            id.append(data[i].sender);
            msg.append(data[i].msg);
            time.append(data[i].time)

            box.append(id);
            box.append(msg);
            box.append(time);

            $(&quot;#msg_box&quot;).append(box);
            $(&quot;#msg_box&quot;).append(line);

        }
    }        
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[Day 86. Spring Framwork 8 : Service Layer]]></title>
            <link>https://velog.io/@ho_c/%EA%B5%AD%EB%B9%84%EA%B5%90%EC%9C%A1-Day-86</link>
            <guid>https://velog.io/@ho_c/%EA%B5%AD%EB%B9%84%EA%B5%90%EC%9C%A1-Day-86</guid>
            <pubDate>Sun, 26 Jun 2022 10:26:29 GMT</pubDate>
            <description><![CDATA[<p><strong>오늘은 그동안 미뤄왔던 Service Layer를 배워보자.</strong></p>
<h2 id="service-layer">Service Layer</h2>
<h3 id="1-기본-구조">1. 기본 구조</h3>
<blockquote>
<p>Client – Tomcat – Servlet Filter – DS – Handler Mapper – Handler(@Controller) - <em>*<em>(Service Layer) *</em></em>- DAO – Mapper(XML MyBatis) - DB – DAO – Handler – DS – ViewResolver </p>
</blockquote>
<p>Service Layer는 어렵진 않지만, 굉장히 번거로운 작업으로 Spring MVC의 기본 구조에서 웹 티어와 비즈니스 로직 사이에 존재한다. 물론 엄밀히 말하면 비즈니스 로직에 포함된다.</p>
<h3 id="2-서비스-레이어의-필요성">2. 서비스 레이어의 필요성</h3>
<blockquote>
<p>우리가 개발하는 프로그램은 크게 Web Tier와 Business Logic으로 구분해서 작업한다. 근데 작업 중에는 양쪽 둘 다 아닌 애매한 작업들이 존재하는데,** 이걸 어디에 둬야할까?**</p>
</blockquote>
<p>Spring MVC는 모든 기능을 굉장히 잘게 잘게 쪼개어 차후 이식성와 유지보수성을 확보한다. 그래서 각 레이어를 굉장히 세분화시켜서 관리한다. 그럼 점에서 두 범위에 들어가지 않는 애매한 작업을 두기 위해 <strong>‘서비스 레이어’</strong>를 만든다.</p>
<p>이로 인해 차후 코드 변화가 있을 각 레이어 간의 영향도가 줄어들어, 그 부분만 바꾸도록 한다. </p>
<blockquote>
<p><strong>ex ) 파일 업로드, pagination, DAO 인스턴스 생성…</strong></p>
</blockquote>
<hr>
<h3 id="3-서비스-레이어-사용법">3. 서비스 레이어 사용법</h3>
<h4 id="①-패키지와-서비스-클래스-생성">① 패키지와 서비스 클래스 생성</h4>
<p>서비스 레이어를 보관할 패키지와 레이어 역할을 할 클래스를 생성한다. 기본적으로 DAO 한 개에 레이어 한 개로 잡는다.</p>
<h4 id="②-서비스-어노테이션-설정">② 서비스 어노테이션 설정</h4>
<p>이 역시 인스턴스이기 때문에 톰캣이 스프링 풀을 만들면서 함께 생성되도록 만든다. 그래서 이를 위해 @Repository로 DAO를 관리하듯이 @Service를 붙여 서비스 레이어를 관리한다.</p>
<pre><code class="language-java">@Service
public class MessagesService {
}</code></pre>
<h4 id="③-dao-di">③ DAO DI</h4>
<p>기존에 컨트롤러에서 DAO를 호출했다면, 이제는 서비스가 DAO를 호출에 값을 받고, 그 값을 돌려주는 형태이다. 때문에 @Autowired로 DAO를 DI하여 사용한다.</p>
<pre><code class="language-java">@Autowired
private MessagesDAO dao;

@Autowired
private FilesDAO fdao;</code></pre>
<h4 id="④-메서드-설정">④ 메서드 설정</h4>
<p>이제 서비스 레이어에서 DAO를 호출하고 다음과 같이 애매한 작업들을 처리한다.</p>
<pre><code class="language-java">
@Transactional
public void insert(String writer, String content, String title, String realPath, MultipartHttpServletRequest mtfRequest) throws Exception {

    // 게시글 처리
    BoardDTO dto = new BoardDTO();
    dto.setWriter(writer);
    dto.setContent(content);
    dto.setTitle(title);

    int pseq = dao.insert(dto);

    // 파일 처리
    File realPathFile = new File(realPath);

    if(!realPathFile.exists()) {realPathFile.mkdir();}

    if(mtfRequest != null) {
        List&lt;MultipartFile&gt; fileList = mtfRequest.getFiles(&quot;fileList&quot;);
        for(int i=0; i&lt;fileList.size(); i++) {
            MultipartFile multi = fileList.get(i);

            String oriName = multi.getOriginalFilename();
            String sysName = UUID.randomUUID()+&quot;_&quot;+oriName;

            multi.transferTo(new File(realPath+&quot;/&quot;+sysName));

            System.out.println(&quot;저장된 파일명 : &quot; + oriName );
            System.out.println(&quot;시스템 파일명 : &quot; + sysName );

            FileDTO fDTO = new FileDTO(0, oriName, sysName, pseq);

            dao.insertFileData(fDTO);
        }
    }    
}

public int update(String title, String content, String seq) {

    int pseq = Integer.parseInt(seq);

    BoardDTO dto = new BoardDTO();

    dto.setTitle(title);
    dto.setContent(content);
    dto.setSeq(pseq);

    return    dao.update(dto);
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[Day 85. Spring Framwork 7 : Mybatis 동적 쿼리]]></title>
            <link>https://velog.io/@ho_c/%EA%B5%AD%EB%B9%84%EA%B5%90%EC%9C%A1-Day-85</link>
            <guid>https://velog.io/@ho_c/%EA%B5%AD%EB%B9%84%EA%B5%90%EC%9C%A1-Day-85</guid>
            <pubDate>Sun, 26 Jun 2022 10:21:31 GMT</pubDate>
            <description><![CDATA[<h2 id="mybatis-동적-쿼리">MyBatis 동적 쿼리</h2>
<p>지난 시간에는 MyBatis를 설치하고, CRUD를 구현하는 것까지 해봤다. 막상 해보고 나면 적당히 편해졌을 뿐, XML갔다가, DAO갔다 왔다 갔다 하는 것도 굉장히 귀찮다.</p>
<blockquote>
<p><em><strong>그래서 굳이? 이걸 쓰는 것에 대한 이유를 못 느꼈다.</strong></em> </p>
<blockquote>
<p>그리고 수업 끝난 뒤, 이래서 쓰는구나 라고 느꼈다. 왜냐면 오늘은 동적쿼리를 만드는 방법을 배웠기 때문이다. <strong>그럼 동적 쿼리는 뭘 말하는걸까?</strong></p>
</blockquote>
</blockquote>
<p>별건 없다. 기존에 우리가 쿼리를 고정시키고 그 안의 값만 바꿔줬다면, 이제는 특정 조건에 따라서 동적으로 쿼리문 자체를 바꿔줄 수 있게 되는데, 이게 동적 쿼리다.</p>
<p><strong>무튼 MyBatis의 동적쿼리 구현법은 크게 3가지가 있다.</strong></p>
<h3 id="1-if-test조건">1. <code>&lt;if test=“조건”&gt;</code></h3>
<p>가장 간단한 방법은 if로 조건에 따라서 쿼리 뒤 쪽으로 붙여주는 법이다. </p>
<pre><code class="language-xml">&lt;!-- 동적 쿼리 --&gt;
&lt;select id=&quot;selectByCon&quot; resultType=&quot;kh.spring.dto.MessagesDTO&quot;&gt;
    select * from messages 
    &lt;if test=&quot;val!=null&quot;&gt;
        where ${col} like &#39;%&#39;||#{val}||&#39;%&#39;
    &lt;/if&gt;     
&lt;/select&gt;</code></pre>
<p>위 텍스트는 <code>select * from messages where ${col}=#{val}</code> 라는 쿼리문을 동적 쿼리로 구현한 거다. 일단 SQL에서 조건은 where절로 붙는다. 따라서 DAO에서 값을 넘겨줄 때, 해당 키가 빈값이 아닐 때만, where절이 붙어 조건 검색이 가능하도록 했다.</p>
<hr>
<h3 id="2-trim">2. <code>&lt;trim&gt;</code></h3>
<p>근데 <code>&lt;if&gt;</code> 의 한계는 조건식이 아주 간단할 때나 편리하다는 거다. 만약 아주 세밀하게 조건에 따라 하려면 자바에 쓸 if를 XML로 옮겨담는 것 뿐이다. 이걸 직접 세미에서 자바 프로그램 내에서 해봤는데, 최소 60개의 조건이 붙는다. 궁금하면 나의 <a href="https://velog.io/@ho_c/%EA%B5%AD%EB%B9%84%EA%B5%90%EC%9C%A1-%EC%84%B8%EB%AF%B8%ED%94%84%EB%A1%9C%EC%A0%9D%ED%8A%B8-%ED%9A%8C%EA%B3%A0">&#39;세미 회고&#39;</a>를 한번 찾아보자.</p>
<p>무튼 그런 복잡하고, 지저분한 방법에서 탈출하는 방법이 <code>&lt;trim&gt;</code>이다. trim은 쉽게 보면 미리 입력해준 쿼리문을 유동적으로 조작해주는 것이다. 특히 조건과 조건이 <code>and</code>로 연결되는 SQL에서는 이 기능이 제일 깔삼하다.</p>
<pre><code class="language-xml">&lt;select id=&quot;selectByMultiCon&quot; resultType=&quot;kh.spring.dto.MessagesDTO&quot;&gt;
    select * from messages

    &lt;if test=&quot;writer !=  null &quot;&gt;
        where writer = #{writer}
    &lt;/if&gt;

    &lt;if test=&quot;content != null&quot;&gt;
        and content = #{contents}
    &lt;/if&gt;
&lt;/select&gt;</code></pre>
<p>직관적으로 알 수 있게끔, if만 사용한 지저분한 쿼리문을 가져와봤다. 각 조건을 구별했는데, 별반 문제 없어오비지만, writer가 없고, content 가 있다면 SQL 에러 바로난다. 또 content 기준으로 검색도 어렵다.</p>
<blockquote>
<p>이럴 때 trim을 사용한다.</p>
</blockquote>
<pre><code class="language-틔">&lt;select id=&quot;selectByMultiCon&quot; resultType=&quot;kh.spring.dto.MessagesDTO&quot;&gt;
    select * from messages
    &lt;trim prefix=&quot;where&quot; prefixOverrides=&quot;and|or&quot;&gt;
        &lt;if test=&quot;writer!=&#39;&#39;&quot;&gt; &lt;!-- DAO에서 넘겨받는 값 --&gt;
            writer=#{writer}
        &lt;/if&gt;
        &lt;if test=&quot;content!=&#39;&#39;&quot;&gt;
            and content=#{content}
        &lt;/if&gt;
    &lt;/trim&gt;
&lt;/select&gt;    </code></pre>
<h4 id="1-안쪽에-태그가-있고-쿼리가-나올-때만-활성화-된다">1) 안쪽에 태그가 있고 쿼리가 나올 때만 활성화 된다.</h4>
<p>일단 trim 자체는 쿼리문을 뽑아서 뒤에 붙여준다고 생각하는게 편하다. 그래서 내부에는 쿼리를 뽑아줄 일종의 조건이 담긴 태그들이 있다.</p>
<p>그리고 포인트는 2가지가 있는 prefix, prifixOverriedes라는 속성이다.</p>
<h4 id="2-접두사-prefix--where">2) 접두사 prefix = “where”</h4>
<blockquote>
<p>“뒤쪽에서 나온 Text 앞에 where을 붙여주세요”</p>
</blockquote>
<p>DAO에서 넘겨준 값과 if의 조건에 따라서 trim 내부에서 text가 나오면 그 맨 앞에 ‘where’을 붙여주라는 것이다. 즉, 출력 시 자동으로 접두사를 붙여주는 건데 spring의 view resolver 와 비슷한 기능을 한다.</p>
<h4 id="3-prefixoverriedesand">3) prefixOverriedes=“and”</h4>
<blockquote>
<p>“뒤쪽에 나올 text의 앞에 and가 나오면 없애주세요”</p>
</blockquote>
<p>실행 순서상 prefixOverriedes 속성이 prefix보다 먼저 실행된다. 만약 조건이 충족돼 해당 쿼리문이 나오는데, 앞에 ‘and’가 붙어 있으면 그걸 자동으로 삭제하고 where을 붙여준다.</p>
<p>결과적으로 조건 자체는 각각 구별해서 확인한다. 거기서 참인 조건들만 모아서 순서대로 text 한 문장을 trim이 뽑아내는데, 이때 prefixOverriedes 조건으로 한번 거르고, prefix를 붙여줘서 쿼리를 날리는 것이다.</p>
<p>때문에 우리는 아주 쉽게 4개의 조건에 따른 쿼리문을 만들 수 있다.</p>
<hr>
<h3 id="3-selectkey">3. <code>&lt;selectKey&gt;</code></h3>
<p>엄밀히 말하면 쿼리문을 조작하는 것이기 아니라서 동적쿼리라고 할 수 없다. 다만 동적으로 2가지 작업을 한 번에 처리해주는 편리한 기능이기 때문에 ‘동적 쿼리’ 파트에 넣어봤다. </p>
<p>상황을 하나 가정했다.</p>
<blockquote>
<p>DB에서 하나의 값을 두 군데에 사용하는 경우가 은근 있다. 이때, 자바에서는 미리 뽑아놓고 분배한다. select seq.nextval from dual; 이 쿼리를 응용해서 말이다.</p>
</blockquote>
<p>하지만 그 방식은 너무 불편하기 때문에 한번에 쿼리를 날려 값을 뽑아서 다음 쿼리를 넘기는 방법을 우리 모두 원한다. 그리고 이 기능을 MyBatis가 지원한다.</p>
<pre><code class="language-xml">&lt;insert id=“insert”&gt;
    insert into messages values(messages_seq.nextval, #{writer}, #{content}, sysdate)
    &lt;selectKey order=“AFTER” keyProperty=“seq” resultType=“int”&gt;
        select messages_seq.currval from dual
    &lt;/selectKey&gt;
&lt;/insert&gt;</code></pre>
<p>selectKey 태그는 3가지 속성이 세팅된다.</p>
<h4 id="1-order">1) order</h4>
<p>order의 값을 해당 쿼리가 실행되는 시점을 의미한다. 그 값으로 ‘BEFORE’, ‘AFTER’을 세팅해서 자신이 속한 쿼리의 실행 시점을 정해줄 수 있다.</p>
<h4 id="2-keyproperty">2) keyProperty</h4>
<p>resultType과는 약간 다른 개념으로, 쉽게 생각하면 값을 받을 바구니를 미리 세팅해주는 것이다. 예를 들어 resultType이 ‘물’이라면 ‘물통’을 준비하는거라 생각하면 된다.</p>
<p>그래서 반환값이 DTO면 DTO이고, 그 안에 멤버필드에 값을 세팅해주는 것이다. 이 원리는 Call by references 기법으로 가능하다.</p>
<p>물론 복잡하지만 Map도 지원하기 때문에 Map으로 받아서 key를 세팅하면된다. 참고로 select을 Map으로 하면 반환값(Object)이니, 형변환해서 사용해주자.</p>
<h4 id="3-resulttype">3) resultType</h4>
<p>앞서 말한 것처럼, 물통이니까 물을 담아야 한다. 그러니 담아올 것이 물이라는 것을 알려주는 것이다.</p>
<p>값을 넣어주고 insert / 값을 출력하고 select 의 과정에 사용되는 DTO는 한 개다.
이 원리는 Call by references 기법으로 가능하다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Day 84. Spring Framwork 6 : Mybatis]]></title>
            <link>https://velog.io/@ho_c/%EA%B5%AD%EB%B9%84%EA%B5%90%EC%9C%A1-Day-84</link>
            <guid>https://velog.io/@ho_c/%EA%B5%AD%EB%B9%84%EA%B5%90%EC%9C%A1-Day-84</guid>
            <pubDate>Sun, 26 Jun 2022 10:14:55 GMT</pubDate>
            <description><![CDATA[<p>지난 시간 파일 업로드, 다운로드를 기점으로 Spring 프레임워크 수업은 일단락됐다. 그래서 오늘부턴 MyBatis 프레임워크를 통해 백 단에서의 DB 작업을 경량화시키면서, 동적 쿼리까지 구현했다.</p>
<h2 id="1-mybatis-개요">1. MyBatis 개요</h2>
<p>일단 이제까지 내가 DB를 다루기 위해 배운 자바쪽 스택은 다음과 같다.</p>
<p>JDBC → DBCP → Singleton, JNDI → Spring JDBC </p>
<p>처음 배울 때는 JDBC의 Connection 인스턴스로 DB와 연결하고, 쿼리를 보낸 뒤, 반환 값을 반복문으로 DTO에 담거나, commit하고 연결 닫고, 이런 과정의 반복이었다. </p>
<p>그래서 첫째로 DB 연결을 관리하기 위해 DBCP를 적용했고, 추후엔 의존성을 낮추고 연결 포화를 막기 위해 싱글톤과 JNDI를 사용했었다.</p>
<p>이후 스프링으로 넘어와서, DTO에 값을 옮겨담는 작업, Statement에 필요한 값을 세팅하는 걸 스프링 JDBC로 간편하게 진행했다. </p>
<p>하지만 문제는 우리가 값을 세팅해야 한다는 것. 즉, 자바라는 틀 안에 갇혀있다는 것이다. 따라서 자바라는 틀에서 벗어나 값을 세팅하는 작업도 가변적으로 진행하기 위해  MyBatis 프레임워크를 배울 것이다.</p>
<hr>
<h2 id="2-mybatis-이점">2. MyBatis 이점</h2>
<ul>
<li>복잡한 데이터 조회에 대해 동적 쿼리를 쉽게 만들 수 있다.</li>
<li>코드 분량을 Spring JDBC보다 더 축소할 수 있다.</li>
<li>Cross Language로 언어의 제약이 없어 타 플랫폼 간의 Migration에 유리하다.</li>
<li>따라서 자바의 영역에 쿼리문을 두지 않는다.</li>
</ul>
<hr>
<h2 id="3-mybatis-초기-세팅">3. MyBatis 초기 세팅</h2>
<p>MyBatis를 사용하기 위해선 기본 세팅이 필요하다. 단계별로 진행해보자</p>
<h3 id="1-기존-db-dependency-가져오기">1) 기존 DB Dependency 가져오기</h3>
<blockquote>
<p>JDBC, DBCP, Spring JDBC</p>
</blockquote>
<p>기존에 사용하던 Dependency들 모두, 서로 연결되어 DB 작업이 가능하게 된다. MyBatis도 이를 그대로 갖고 DB 작업을 하기에 그대로 pom.xml에 넣어준다.</p>
<pre><code class="language-xml">&lt;!-- Data Related --&gt;         
&lt;dependency&gt;
   &lt;groupId&gt;com.oracle.database.jdbc&lt;/groupId&gt;
    &lt;artifactId&gt;ojdbc8&lt;/artifactId&gt;
    &lt;version&gt;21.1.0.0&lt;/version&gt;
&lt;/dependency&gt;

&lt;dependency&gt;
    &lt;groupId&gt;org.apache.commons&lt;/groupId&gt;
    &lt;artifactId&gt;commons-dbcp2&lt;/artifactId&gt;
    &lt;version&gt;2.9.0&lt;/version&gt;
&lt;/dependency&gt;

&lt;dependency&gt; 
    &lt;groupId&gt;org.springframework&lt;/groupId&gt;
    &lt;artifactId&gt;spring-jdbc&lt;/artifactId&gt;
    &lt;version&gt;${org.springframework-version}&lt;/version&gt;
&lt;/dependency&gt;</code></pre>
<br>

<h3 id="2-maven-repository에서-mybatis-mybatis-spring-가져오기">2) Maven repository에서 MyBatis, MyBatis Spring 가져오기</h3>
<p>MyBatis 사용을 위해 Maven repository <code>MyBatis, MyBatis Spring</code> 이 두 개를 가져온다.
이때, MyBatis Spring과 Spring JDBC 둘 다 MyBatis가 Spring에서 동작하기 위해서 필요한 것이니 꼭 넣어줘야 한다.</p>
<pre><code class="language-xml">&lt;!-- MyBatis --&gt;
&lt;dependency&gt;
    &lt;groupId&gt;org.mybatis&lt;/groupId&gt;
    &lt;artifactId&gt;mybatis&lt;/artifactId&gt;
    &lt;version&gt;3.5.10&lt;/version&gt;
&lt;/dependency&gt;        

&lt;!-- MyBatis Spring--&gt;
&lt;dependency&gt;
    &lt;groupId&gt;org.mybatis&lt;/groupId&gt;
    &lt;artifactId&gt;mybatis-spring&lt;/artifactId&gt;
    &lt;version&gt;2.0.7&lt;/version&gt;
&lt;/dependency&gt;</code></pre>
<br>

<h3 id="3-root-contextxml에서-datasource-bean-가져오기">3) Root-context.xml에서 DataSource bean 가져오기.</h3>
<p>Spring JDBC처럼 DB 연결도 MyBatis가 관리하기 때문에, 기존과 같이 DBCP 인스턴스도 가져온다.</p>
<pre><code class="language-xml">&lt;bean id=&quot;dataSource&quot; class=&quot;org.apache.commons.dbcp2.BasicDataSource&quot;&gt;
    &lt;property name=&quot;driverClassName&quot; value=&quot;oracle.jdbc.driver.OracleDriver&quot;&gt;&lt;/property&gt;
    &lt;property name=&quot;url&quot; value=&quot;jdbc:oracle:thin:@Localhost:1521:xe&quot;&gt;&lt;/property&gt;
    &lt;property name=&quot;username&quot; value=&quot;ID&quot;&gt;&lt;/property&gt;
    &lt;property name=&quot;password&quot; value=&quot;PW&quot;&gt;&lt;/property&gt;
&lt;/bean&gt;</code></pre>
<br>

<h3 id="4-mybatis-인스턴스-만들어주기">4) MyBatis 인스턴스 만들어주기</h3>
<p>이제 우리가 사용할 MyBatis 인스턴스를 생성해주자. Maven로 가져왔으니까, root-context.xml에다가 세팅해주면 된다.</p>
<p>MyBatis 인스턴스는 2개가 필요하다. <code>SqlSessionFactoryBean</code> <code>SqlSessionTemplate</code> 인데, 실제 사용하는 건 후자고 전자는 후자를 만드는 공장이라고 생각하면 된다.</p>
<p><em><strong>[ SqlSessionFactoryBean ]</strong></em></p>
<pre><code class="language-xml">&lt;bean id=&quot;sqlSessionFactory&quot; class=&quot;org.mybatis.spring.SqlSessionFactoryBean&quot;&gt;
    &lt;property name=&quot;dataSource&quot; ref=&quot;dataSource&quot;/&gt;
    &lt;property name=&quot;mapperLocations&quot; value=&quot;classpath:/mybatis/*-mapper.xml&quot;/&gt;        
&lt;/bean&gt;</code></pre>
<h4 id="1-dbcp-di">(1) DBCP DI</h4>
<p><code>SqlSessionFactoryBean</code> 에는 두 개의 인자가 들어간다. 하나는 앞서 만든 DBCP 인스턴스이고, 다른 하나는 쿼리문을 읽어올 XML 파일의 주소이다.</p>
<h4 id="2-mapperlocations-di">(2) mapperLocations DI</h4>
<p>앞서 MyBatis는 이식성을 위해 쿼리문을 자바의 영역에 두지 않는다고 배웠다. 즉, DB 작업을 하기 위해 쿼리문이 필요한데, MyBatis는 쿼리문은 XML파일에 다가 미리 설정해둔다. 따라서 이 쿼리문을 가져오기 위한 해당 파일의 주소를 지정해준다.</p>
<p>이러면 자바가 classpath 코드를 통해서 이클립스(개발환경)가 갖고 있는 클래스패스를 불러오고, 이 안에서 쿼리문을 담아둔 XML문서를 찾아낸다.</p>
<h4 id="3-sqlsessiontemplate-생성">(3) SqlSessionTemplate 생성</h4>
<p>이렇게 세팅을 하고나면, 실제 우리가 자바의 영역에서 쓸 인스턴스를 생성해야 한다. 그게 바로 <code>SqlSessionTemplate</code>인데, Setter를 사용하지 않고, 생성자를 통해 SqlSessionFactoryBean를  DI한다.</p>
<p><em><strong>[ SqlSessionTemplate ]</strong></em></p>
<pre><code class="language-xml">&lt;bean id=&quot;sqlSession&quot; class=&quot;org.mybatis.spring.SqlSessionTemplate&quot;&gt;
    &lt;constructor-arg name=&quot;sqlSessionFactory&quot; ref=&quot;sqlSessionFactory&quot;/&gt;
&lt;/bean&gt;    </code></pre>
<br>

<h3 id="5-mapper-문법을-인식할-수-있는-문법-정보-import-해주기">5) mapper 문법을 인식할 수 있는 문법 정보 import 해주기</h3>
<p>우리가 쿼리를 작성하기 위해 XML파일을 설정한 클래스패스에  생성하면 <code>&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; ?&gt;</code> 와 같은 문구만 덩그러니 있다.</p>
<p>다짜고짜 SQL문을 적으면 될 것 같지만, 데이터를 분석하기 위해서는 기존 세팅과 태그를 만들어서 컴퓨터에게 넘겨줘야 한다. 이때 쓰이는 게 <code>mapper</code> 태그인데, 이걸 프로그램이 인식할 수 있도록 해당 태그에 대한 문법 정보를 끌어와야 한다.</p>
<pre><code class="language-xml">&lt;!DOCTYPE mapper
  PUBLIC &quot;-//mybatis.org//DTD Mapper 3.0//EN&quot;
  &quot;http://mybatis.org/dtd/mybatis-3-mapper.dtd&quot;&gt;</code></pre>
<p>위 텍스트는 Mybatis 공식 문서에 가면 찾을 수 있다.</p>
<br>

<h3 id="6-namespace-정해주기">6) namespace 정해주기</h3>
<p>프로그램이 문서의 내용을 해석할 정보를 주었다면, 이제는 일종의 맵핑을 해야 한다. 즉, 테이블에 따라 쿼리문을 분류해줘야 하는데, <code>namespace</code>는 그 중에서 대분류라고 생각하면 된다. 
<strong>( 각, 쿼리문에 대한 소분류는 태그들의 id값으로 구분된다. )</strong></p>
<pre><code class="language-xml">&lt;mapper namespace=&quot;Contact&quot;&gt;
    &lt;insert id=&quot;insert&quot;&gt;
        insert into contact values(contact_seq.nextval, #{name}, #{contact}, sysdate)
    &lt;/insert&gt;

    &lt;delete id=&quot;deleteBySeq&quot;&gt;
        delete from contact where seq = #{value}
    &lt;/delete&gt;

    &lt;select id=&quot;selectAll&quot; resultType=&quot;kh.spring.dto.ContactDTO&quot;&gt;
        select * from contact
    &lt;/select&gt;

    &lt;update id=&quot;updateBySeq&quot; parameterType=&quot;kh.spring.dto.ContactDTO&quot;&gt;
        update contact set name = #{name}, contact = #{contact} where seq = #{seq}    
    &lt;/update&gt;

&lt;/mapper&gt;</code></pre>
<hr>
<h2 id="4-mybatis로-crud-만들기">4. MyBatis로 CRUD 만들기</h2>
<p>전체 세팅은 다 끝났고, 이제는 MyBatis를 이용해서 CRUD를 구현해보자. MyBatis를 사용하려면 우리는 두 파일을 다루게 된다. 하나는 쿼리륻 담을 XML, 다른 하나는 자바에서 MyBatis 프레임워크를 사용할 DAO이다.</p>
<p>DAO에는 기본으로 <code>@Autowried</code> 로 MyBatis 인스턴스를 DI해준다.</p>
<pre><code class="language-java">@Autowired
private SqlSession mybatis;</code></pre>
<h3 id="0-값-세팅하기">0) 값 세팅하기</h3>
<p>MyBatis로 쿼리를 날리는 과정은 DAO를 통해서 XML과 세팅할 값을 Mybatis 인스턴스의 멤버 메서드를 넣어주면 자동으로 세팅해 쿼리를 날리고 값을 반환한다.</p>
<p>이때, 중요한 것은 값을 날려주는 방식이다. 이전 단계에서 값을 날려주는, 다르게 세팅하는 방식은 직접 우리가 Setter 메서드를 통해서 쿼리에 값을 하나씩 넣어줬다. 이제는 우리가 값을 보내기만 하면 MyBatis가 진행해준다.</p>
<blockquote>
<p>단, 문제는 MyBatis 메서드에 인자로 값을 넘기는 건 ‘가변 인자’가 아니란 것이다. 이걸 주의하면서 세팅법을 알아보자.</p>
</blockquote>
<h4 id="--기본-자료형">- 기본 자료형</h4>
<p>MyBatis는 객체(DTO, Map)가 아닌 기본 자료형 한 개만 넘어올 경우, <code>#{value}</code> 를 사용하여 값을 세팅한다.</p>
<h4 id="--자료형이-여러-가지일-경우">- 자료형이 여러 가지일 경우</h4>
<p>MyBatis는 Spring JDBC처럼 여러 값을 가변 인자로 받아 세팅하는 것을 지원하지 않아 다수의 값을 다루려면 객체를 사용해야 한다. 때문에 바구니에 담아서 넘겨주는데, 그 바구니의 역할을 하는 것이 DAO, Map이다.</p>
<p><em><strong>① DTO</strong></em></p>
<p>흔히 우리가 데이터를 받아올 때 사용한 데이터 인스턴스이다. 데이터를 넣어주기 적합하지만, 매번 넣어줄 데이터에 맞춰서 DTO를 만들고 모아놔야 한다.</p>
<hr>
<p><em>*<em>② Map *</em></em></p>
<p>컬렉션 중에 하나로 key-value로 세팅할 값(value)과 이를 사용하기 위한 키(key)로 데이터를 분류하기 때문에 전달 용도로 MyBatis에서 지원한다. 굳이 비유하자면 JSON의 자바 버전이라고 생각하는게 편하다.</p>
<h4 id="map의-기본-사용법">Map의 기본 사용법</h4>
<p>맵을 사용하기 위해선 Map 인스턴스를 만들어야 된다. 우리는 Map 중에서도 HashMap을 사용한다.</p>
<pre><code class="language-java">// 인스턴스 생성 &lt;key의 자료형, value의 자료형&gt;
Map&lt;String, String&gt; map = new HashMap&lt;&gt;();</code></pre>
<p>그 후에는 <code>.put</code>을 넣어줘서 Map 인스턴스 안에 값을 세팅해준다. 이때 value의 자료형은 Object도 된다. 정말 다양한 값을 쉽게 보내줄 수 있어서 용이하다.</p>
<pre><code class="language-java">map.put(&quot;id&quot;, “admin”);
map.put(&quot;pw&quot;, “1234”);</code></pre>
<h4 id="mybatis에서-사용법">MyBatis에서 사용법</h4>
<p>HashMap 인스턴스에 값을 다 세팅했다면, MyBatis에게 메서드로 넘겨주기만 하면 된다.</p>
<pre><code class="language-java">return mybatis.selectOne(&quot;Member.login&quot;, map);</code></pre>
<p>이 값을 제대로 사용하기 위해선 Member.login이 맵핑된 XML에서 어떻게 사용할지 우리가 세팅해줘야 한다.</p>
<pre><code class="language-xml">&lt;select id=&quot;login&quot; resultType=&quot;boolean&quot;&gt;
    select count(*) from member where id=#{id} and pw=#{pw}
&lt;/select&gt;</code></pre>
<p>우리가 앞서 세팅한 키를 <code>#{}</code> 안에 넣어주면 끝이다.</p>
<hr>
<blockquote>
<p>그럼 이제 CRUD를 만들어보자.</p>
</blockquote>
<h3 id="1-insert">1) <code>.insert()</code></h3>
<p>일단 DAO에서 DB에 데이터를 넣기 위한 MyBatis 메서드는 <code>.insert()</code>이다. </p>
<pre><code class="language-java">public int insert(ContactDTO dto) throws Exception{

    return mybatis.insert(&quot;Contact.insert&quot;, dto);
}</code></pre>
<p>첫 번째 인자는 우리가 앞서 맵핑해둔, XML파일 내의 <code>namespace</code>와 내부의 작성된 XML 쿼리 태그의 <code>id</code> 값이다.</p>
<p>두 번째 인자는 XML에서 세팅될 객체 또는 값이다.</p>
<pre><code class="language-xml">&lt;mapper namespace=&quot;Contact&quot;&gt;
    &lt;insert id=&quot;insert&quot;&gt;
        insert into contact values(contact_seq.nextval, #{name}, #{contact}, sysdate)
    &lt;/insert&gt;
&lt;/mapper&gt;</code></pre>
<p>위 코드는 XML파일의 코드이다. 앞서 설명한 첫 번째 인자를 따라, 메서드는 해당 쿼리를 인식하게 되고 그 쿼리 안에 두 번째 인자로 받은 DTO의 값을 세팅하게 된다.</p>
<p>값을 세팅할 때는 <code>#{ 변수명 }</code> 으로 세팅하는데 EL처럼 <code>${ 변수명 }</code>도 가능하다. 하지만 둘의 차이는 <code>‘’(홑따옴표)</code>이다. 전자의 쿼리 결과는 <code>‘변수명’</code>의 형태를 띄지만, 후자는 <code>변수명</code>이 되어서 보안 측면에서 쿼리 주입이 가능하기에 위험하다.</p>
<p>결과적으로 객체로 묶어서 보낸 값의 변수명을 그대로 써주게 되면, 자바의 관점에서는 자동으로 <code>#{name} == dto.getName();</code>  같은 Getter를 사용한다고 볼 수 있다.</p>
<hr>
<h3 id="2-select">2) <code>.select()</code></h3>
<p>데이터를 조회하는 select 역시, DAO와 XML 모두 <code>select</code>이라는  키워드를 사용해서 DAO 자체는 크게 달라지지 않는다. 다만 XML에서 반환값을 잘 설정해줘야 한다. 만약 조건을 선택할 경우, insert처럼 2번째 인자로 값을 넘겨주면 된다.</p>
<pre><code class="language-java">public List&lt;ContactDTO&gt; selectAll() throws Exception{

    return mybatis.selectList(&quot;Contact.selectAll&quot;);
}</code></pre>
<pre><code class="language-XML">&lt;select id=&quot;selectAll&quot; resultType=&quot;kh.spring.dto.ContactDTO&quot;&gt;
    select * from contact
&lt;/select&gt;</code></pre>
<p>포인트는 <code>resultType</code>이라는 속성이다. 실제 쿼리를 MyBatis가 받아서 처리해주기 때문에 우리는 MyBatis에 어떤 데이터 타입으로 반환해줘야 하는지 알려줘야 한다. (개발자가 거기까지 생각할 수 없으니까..)</p>
<p>추가로 boolean형의 경우, 반환값이 0이면 false, 1이면 true를 반환해준다.</p>
<hr>
<h3 id="3-delete">3) .delete</h3>
<p>삭제 기능도 별반 다를 건 없다. <code>&lt;delete&gt;</code> 태그를 사용하고 쿼리문을 넣어준 뒤, 맵핑만 하면 된다.</p>
<pre><code class="language-java">public int delete(int seq) throws Exception {

    return mybatis.delete(&quot;Contact.deleteBySeq&quot;, seq);
}</code></pre>
<pre><code class="language-xml">&lt;delete id=&quot;deleteBySeq&quot;&gt;
    delete from contact where seq = #{value}
&lt;/delete&gt;</code></pre>
<hr>
<h3 id="4-update">4) .update</h3>
<p>수정 기능도 다른 건 크게 없다. 다만 select에서 생겼던 고민이 어떤 형태로 쿼리 결과를 받아올지 몰라서 <code>resultType</code> 라는 속성을 썻다면, update는 어떤 형태로 값을 세팅할지 몰라서 <code>parameterType</code>을 사용한다.</p>
<pre><code class="language-java">public int update(ContactDTO dto) throws Exception {

    return mybatis.update(&quot;Contact.updateBySeq&quot;, dto);
}</code></pre>
<pre><code class="language-xml">&lt;update id=&quot;updateBySeq&quot; parameterType=&quot;kh.spring.dto.ContactDTO&quot;&gt;
    update contact set name = #{name}, contact = #{contact} where seq = #{seq}    
&lt;/update&gt;</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[Day 82. Spring Framwork 5 : 파일 관련 기능]]></title>
            <link>https://velog.io/@ho_c/%EA%B5%AD%EB%B9%84%EA%B5%90%EC%9C%A1-Day-82</link>
            <guid>https://velog.io/@ho_c/%EA%B5%AD%EB%B9%84%EA%B5%90%EC%9C%A1-Day-82</guid>
            <pubDate>Sun, 26 Jun 2022 10:04:51 GMT</pubDate>
            <description><![CDATA[<h1 id="spring-file-upload-download">Spring File upload, download</h1>
<blockquote>
<p>이번 시간엔 스프링을 이용해서 파일 업로드-다운로드 기능을 다뤄봤다.
( 너무 진행이 느려서 이틀치라는 건 안 비밀)</p>
</blockquote>
<p>먼저 중점은 파일 업로드을 위해 multi-part/form-data라는 전달 타입을 사용하면, request를 서버 측에서 분석할 수 없다는 것이다. 그래서 세미 때는 Cos.jar를 라이브러리를 사용해서 처리했다.</p>
<p>이 Cos.jar 라이브러리는 편의성이 높지만,  문제는 동일 name값이 들어가면 업로드는 되어도, 파일 정보를 뽑아낼 수 없는 한계가 있다. 그래서 스프링으로 넘어와서는 Apache-fileupload를 사용해서 업로드 기능을 구현하고자 한다.</p>
<hr>
<h2 id="1-maven-dependency-추가">1. Maven dependency 추가</h2>
<p>1.4 ver : <a href="https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload/1.4">https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload/1.4</a></p>
<pre><code class="language-xml">&lt;dependency&gt;
    &lt;groupId&gt;commons-fileupload&lt;/groupId&gt;
    &lt;artifactId&gt;commons-fileupload&lt;/artifactId&gt;
    &lt;version&gt;1.4&lt;/version&gt;
&lt;/dependency&gt;</code></pre>
<p>그러면 Maven Dependencies에 <code>commons-fileupload-1.4.jar</code> 와 <code>commos-io-2.2.jar</code> 가 추가된다. 이렇게 추가된 라이브러리는 스프링에서 지원해서 별다른 설정 없이 <code>bean</code>으로 스프링 컨테이너 등록해주면 된다.</p>
<h2 id="2-servlet-context에-multipartresolver-추가">2. Servlet-context에 multipartResolver 추가</h2>
<p>앞서 말한 것처럼 몇 가지 특정한 라이브러리들은 @Autowired나 @Component처럼 우리가 DI 세팅을 따로 해서 사용할 필요 없이, 스프링이 알아서 인식해 사용한다. 그래서 컨테이너 안에만 넣어주면 되고, 이를 다루기 위해 multipartResolver가 필요하다.</p>
<pre><code class="language-xml">&lt;beans:bean id=&quot;multipartResolver&quot; class=&quot;org.springframework.web.multipart.commons.CommonsMultipartResolver&quot;&gt;
    &lt;beans:property name=&quot;maxUploadSize&quot; value=&quot;10485760&quot;&gt;&lt;/beans:property&gt;
&lt;/beans:bean&gt;</code></pre>
<h2 id="3-upload">3. upload</h2>
<p>이제 우리가 사용하는 request는 자동으로 스프링컨테이너에 라이브러리 빈에 의해 multi-part로 업그레이드 된다. 때문에 컨트롤러 매개변수로 쉽게 받을 수 있다.</p>
<p>하지만 실제 파일은 업로드 과정에서 임시 저장소에 들어간다. 그래서 따로 우리가 저장소에 로직이 끝나기 전에 저장해줘야 한다.</p>
<hr>
<h3 id="0-realpath">0) RealPath</h3>
<p>업로드 되는 파일은 프로젝트 파일이 아닌, 서버가 가동되는 RealPath에서 저장되고 다운로드 된다. 따라서 조작을 위해 RealPath 경로를 가져와야 한다.</p>
<h3 id="1-session에서-가져오기">1) Session에서 가져오기</h3>
<pre><code class="language-java">@Autowired
private HttpSession session; </code></pre>
<h3 id="2-경로-잡기생성">2) 경로 잡기(생성)</h3>
<pre><code class="language-java">String realPath = session.getServletContext().getRealPath(&quot;upload&quot;); // 파일 업로드 경로 설정

File realPathFile = new File(realPath); // 업로드 경로를 파일 객체로 생성

if(!realPathFile.exists()) {realPathFile.mkdir();}; // 경로가 없으면 생성 / 자동완성 안되면 mkdir() 쓰셈</code></pre>
<h3 id="3-파일명-설정">3) 파일명 설정</h3>
<p>파일명은 DB에 저장해서 데이터를 분류하는 용도로 사용하거나, 다운로드 시 특정 파일을 선택할 수 있는 기능을 한다. 그래서 Cos.jar에선 파일명이 겹치지 않는 메서드를 지원했지만,
Apache에는 중복되는 이름을 변경하는 기능이 없어 애초에 겹치지 않게 만들어줘야 한다.</p>
<pre><code class="language-java">String oriName = file.getOriginalFilename(); // 클라이언트에서 보여질 이름
String sysName = UUID.randomUUID()+&quot;_&quot;+oriName; // 해당 메서드는 중복되지 않는 랜덤한 String값을 만들어 반환한다.</code></pre>
<h3 id="4-파일-저장-저장소로-보관">4) 파일 저장 (저장소로 보관)</h3>
<pre><code class="language-java">file.transferTo(new File(realPath+&quot;/&quot;+sysName));</code></pre>
<h3 id="5-db-작업">5) DB 작업</h3>
<p>이렇게 들어온 파일들은 데이터 관리를 위해 DB에 파일 정보를 저장한다. 이를 위해 root-context.xml에서 DBCP, Spring JDBC의 빈을 생성하고 DI작업, DAO의 @Repository설정, JdbcTemplate @Autowired 설정, DTO 생성을 처리하자. </p>
<blockquote>
<p>라이브러리는 Maven Repository에서 가져온다.</p>
</blockquote>
<p><strong><em>[ pom.xml ]</em></strong></p>
<pre><code class="language-xml">&lt;dependency&gt;
    &lt;groupId&gt;com.oracle.database.jdbc&lt;/groupId&gt;
    &lt;artifactId&gt;ojdbc8&lt;/artifactId&gt;
    &lt;version&gt;21.1.0.0&lt;/version&gt;
&lt;/dependency&gt;

&lt;dependency&gt;
    &lt;groupId&gt;org.apache.commons&lt;/groupId&gt;
    &lt;artifactId&gt;commons-dbcp2&lt;/artifactId&gt;
    &lt;version&gt;2.9.0&lt;/version&gt;
&lt;/dependency&gt;

&lt;dependency&gt;
    &lt;groupId&gt;org.springframework&lt;/groupId&gt;
    &lt;artifactId&gt;spring-jdbc&lt;/artifactId&gt;
    &lt;version&gt;${org.springframework-version}&lt;/version&gt;
&lt;/dependency&gt;</code></pre>
<p><em><strong>[ FileDAO ]</strong></em></p>
<pre><code class="language-java">@Repository
public class FilesDAO {

    @Autowired
    private    JdbcTemplate jdbc;

    public int insert(FilesDTO dto) throws Exception{
        String sql = &quot;insert into files values(files_seq.nextval, ?, ?, ?)&quot;;
        return jdbc.update(sql, dto.getOri_name(), dto.getSys_name(),dto.getParent_seq());
    }
}</code></pre>
<p><em>**[ FileDTO ]
**</em></p>
<pre><code class="language-java">public class FilesDTO {

    private int seq;
    private String ori_name;
    private String sys_name;
    private int parent_seq;

    public FilesDTO() { }
}
// 명시 생성자랑 Setter, Getter는 알아서...ㅎ</code></pre>
<p>다시 FileController로 돌아와서, DB에 정보입력 작업을 마무리한다. 만약 실행 후에 에러가 난다면 Servlet-context에서 <code>component-scan</code>을 제대로 설정했는지 확인해보자.</p>
<hr>
<h3 id="6-여러-개의-옵션-추가-multiple">6) 여러 개의 옵션 추가 <code>multiple</code></h3>
<p>아파치 라이브러리와 스프링으로 인해 이제 프론트에서 여러 개의 파일을 올릴 수 있다.
먼저 파일을 여러 개 업로드 하기 위해선 <code>multiple</code> 조건을 걸어주면 된다.</p>
<pre><code class="language-html">&lt;input type=file name=file multiple&gt;</code></pre>
<p>이제 업로드는 여러 개가 된다. 이를 컨트롤러에서 받아줘야 하는데, 기존처럼 MultipartFile 인스턴스 하나로는 다수의 파일을 담을 수 없다. 따라서 배열을 이용하여 담아준다. </p>
<pre><code class="language-java">@RequestMapping(&quot;upload&quot;)
public String upload(String writer, String message, MultipartFile[] file) throws Exception{

    String realPath = session.getServletContext().getRealPath(&quot;upload&quot;); 

    File realPathFile = new File(realPath); 

    if(!realPathFile.exists()) {realPathFile.mkdir();}; 

    // forEach로 반복 작업을 해주면 된다.    
    for(MultipartFile mf : file) {

        String oriName = mf.getOriginalFilename();
        String sysName  = UUID.randomUUID()+&quot;_&quot;+oriName;

        mf.transferTo(new File(realPath+&quot;/&quot;+sysName));

        dao.insert(new FilesDTO(0, oriName, sysName, 0)); // DB작업
    }

    return &quot;redirect:/&quot;;
}
</code></pre>
<hr>
<h2 id="4-download">4. download</h2>
<p>먼저 프론트에 저장된 파일들의 이름을 꺼내올 수 있도록 UI를 만들어준다.</p>
<pre><code class="language-html">&lt;fieldset&gt;
    &lt;legend&gt;File List&lt;/legend&gt;
    &lt;button id=fileList&gt;파일목록&lt;/button&gt;    
&lt;/fieldset&gt;</code></pre>
<p>디자인은 잼병이라...대충 만들어 준다.</p>
<p>이제 ajax를 이용해서 저 위에 ‘파일목록’버튼을 누를 때마다, DB에서 정보를 꺼내오게 로직을 짜줄 것이다. 그리 어렵진 않으니 간단히 해보자.</p>
<p><em><strong>[ ajax ]</strong></em></p>
<pre><code class="language-javascript">$(&quot;#fileList&quot;).on(&quot;click&quot;, function(){
    $.ajax({
        url: &quot;/file/fileList&quot;,
        dataType : &quot;json&quot;
    }).done(function(resp){

        for(let i=0; i&lt;resp.length; i++){

            let fileLine = $(&quot;&lt;div&gt;&quot;);
            fileLine.addClass  = &quot;fileItem&quot;;

            let fileAnker = $(&quot;&lt;a&gt;&quot;);
            fileAnker.attr(&quot;href&quot;, &quot;/file/download?ori_name=&quot;+resp[i].ori_name+&quot;&amp;sys_name=&quot;+resp[i].sys_name);
            fileAnker.text(resp[i].ori_name);

            fileLine.append(fileAnker);
            $(&quot;#fileList&quot;).after(fileLine);

        }    
    });
});</code></pre>
<p>해당 요청은 fileList라는 URL로 요청을 하면 컨트롤러에서 JSON 형태로 응답하도록 만들었다. 그리고 그 응답 내용을 동적 바인딩으로 프론트의 <code>id=fileList</code> 버튼 뒤로 요소가 들어가게 설정하고 <code>&lt;a&gt;</code> 태그의 속성도 추가해서 다운로드 요청도 설정해놨다.</p>
<p><em><strong>[ Controller ]</strong></em></p>
<pre><code class="language-java">@ResponseBody
@RequestMapping(&quot;fileList&quot;)
public String fileList() throws Exception{

    Gson g = new Gson();

    List&lt;FilesDTO&gt; list = dao.getList();

    String result = g.toJson(list);

    return result;
}</code></pre>
<p>응답은 JSON으로 보내기 위해서 Maven Repository에서 GSON 2.9.0버전을 가져왔다. 음..그리고 미리 bean으로 생성해서 사용할까 했지만, ajax 쓰는 메서드가 한 개 뿐이라 그냥 new해버렸다.</p>
<blockquote>
<p>이렇게 GSON을 활용하면, 객체 배열도 쉽게 JSON의 형태로 직렬화해서 프론트로 보내줄 수 있다. </p>
</blockquote>
<hr>
<h3 id="1-jackson">1) Jackson</h3>
<p>하지만 Spring은 GSON보드는 jackSon을 더 권장한다. 위 과정을 Jackson으로 한번 바꿔서 진행해보자. 먼저 기존의 GSON dependency는 지워주고, Maven Repository에 들어가 <code>jackson-databind</code> 최신 버전을 받아준다.</p>
<p><strong>그리고 컨트롤러의 내용을 다음과 바꿔준다.</strong></p>
<pre><code class="language-java">@ResponseBody
@RequestMapping(&quot;fileList&quot;)
public List&lt;FilesDTO&gt; fileList() throws Exception{

    List&lt;FilesDTO&gt; list = dao.getList();

    return list;
}</code></pre>
<p>Jackson은 Spring에서 공식지원하기 때문에 별다른 과정이 필요 없다. List와 같은 형태로 메서드 반환을 해주면, 자동으로 Jackson에서 직렬화 처리해준다. 그래서 굳이 새로운 객체를 사용하지 않는 이상 Jackson이 편하고, 임의의 객체를 만들어 처리하면 GSON이나, ObjectMapper를 사용하기를 추천한다.</p>
<hr>
<h3 id="2-다운로드-컨트롤러">2) 다운로드 컨트롤러</h3>
<p>Spring에서 파일 다운로드는 DS를 거치지 않고, 파일 스트림을 그냥 열어서 돌려주기 때문에 반환값은 void로 해도 괜찮다. 그리고 들어오는 값을 받을 name과 스트림을 돌려줄 reponse 객체도 미리 준비해둔다.</p>
<p><strong>(1) 경로 가져오기</strong></p>
<p>파일을 저장한 경로를 서버의 리얼패스에서 끌어온다.</p>
<pre><code class="language-java">String realPath = session.getServletContext().getRealPath(&quot;upload&quot;);</code></pre>
<p><strong>(2)  파일 타겟팅</strong>
클라이언트에서 요청한 파일의 정보를 파일 객체에 담아 준다. 한글 파일의 경우엔 깨지지 않도록 인코딩 처리를 해준다.</p>
<pre><code class="language-java">File targetFile = new File(realPath+&quot;/&quot;+sys_name); 
ori_name = new String(ori_name.getBytes(&quot;utf8&quot;), &quot;ISO-8859-1&quot;);</code></pre>
<p><strong>(3) response 객체 설정</strong></p>
<ul>
<li>먼저 객체의 용도가 바뀌었기 때문에 설정을 초기화 한다.</li>
<li>클라이언트에서 다운 받을 때, 해당 파일의 이름을 세팅해준다.</li>
</ul>
<pre><code class="language-java">response.reset();
response.setHeader(&quot;content-disposition&quot;, &quot;attachment;filename=\&quot;&quot;+ori_name+&quot;\&quot;;&quot;);</code></pre>
<p><strong>(4) Stream을 열고 보내기</strong></p>
<p>먼저 try-with-resource로 스트림이 자동으로 닫히도록 틀을 세워 준다. 그리고 <code>input, output</code> 스트림을 다루기 편하도록 업그레이드 시켜준 뒤, 메모리에 쓰고 전송한다.</p>
<pre><code class="language-java">        try(
                DataInputStream dis = new DataInputStream((new FileInputStream(targetFile))); // 객체를 메모리로 올리는 길
                DataOutputStream dos = new DataOutputStream(response.getOutputStream()); // Servlet outputStream을 불러와 업그레이드해서 내보낼 준비
                ){
            byte[] fileContents = dis.readAllBytes(); // 파일을 담을 배열을 만들주고
            dos.write(fileContents); // // 메모리에 쓴 뒤
            dos.flush(); // 메모리에 있는 내용을 전송하고 버퍼를 비움
        }</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[Day 81. Spring Framwork 4 : Spring JDBC]]></title>
            <link>https://velog.io/@ho_c/%EA%B5%AD%EB%B9%84%EA%B5%90%EC%9C%A1-Day-81</link>
            <guid>https://velog.io/@ho_c/%EA%B5%AD%EB%B9%84%EA%B5%90%EC%9C%A1-Day-81</guid>
            <pubDate>Sun, 26 Jun 2022 09:56:38 GMT</pubDate>
            <description><![CDATA[<h2 id="spring-jdbc">Spring JDBC</h2>
<p>현재 사용하는 JDBC는 반복되고, 작성할 코드가 너무 많다. 그래서 개발자인 내가 해야될 일이  너무 많아서, 이번엔 스프링 전용 JDBC를 사용해 DB작업을 더 간소화시켜볼 것이다. </p>
<blockquote>
<p>물론 DB전용 프레임워크에 비해 성능은 낮지만, 충분히 지금 단계에서는 써먹도 좋다.</p>
</blockquote>
<h3 id="1-dependency-가져오기">1) Dependency 가져오기</h3>
<p>먼저 Spring JDBC를 사용하기 위해 Maven에서 라이브러리 태그를 불러와서, pom.xml에 추가해주자.</p>
<pre><code class="language-xml">&lt;!-- Spring JDBC --&gt;
&lt;dependency&gt;
    &lt;groupId&gt;org.springframework&lt;/groupId&gt;
    &lt;artifactId&gt;spring-jdbc&lt;/artifactId&gt;
    &lt;version&gt;${org.springframework-version}&lt;/version&gt;
&lt;/dependency&gt;</code></pre>
<p>이때, <code>&lt;version&gt;</code> 은 문서 최상단의 <code>&lt;properties&gt;</code> 내부의 태그를 OGNL 방식으로 문서 안에서 일종의 ‘환경 변수’로 사용한다. 이 역시 버전의 통일성과 최적화, 유지 보수를 위해 기존에 세팅해둔 값을 사용한다.</p>
<hr>
<h3 id="2-spring-jdbc-bean">2) Spring JDBC Bean</h3>
<p>스프링 JDBC의 목적은 기존 DB작업을 더 간결화하는 것이다. 쉽게 말하면 최적화? 곧 스프링에서 DB작업 자체를 우리가 세팅해주는 값에 따라서 진행해주는 것이다. </p>
<p>그 결과로 우리가 하나, 하나 <code>pstat.setString();</code>으로 값을 세팅하고, DBCP에서 <code>Connection</code> 인스턴스 뽑아오는, 이런 과정이 간소화된다.</p>
<hr>
<h3 id="3-dbcp-di">3) DBCP DI</h3>
<p>그래서 첫째로 Spring JDBC bean에 DBCP bean을 DI해줘야 한다. 이로 인해 DBCP 역시 우리의 손을 떠나 Spring JDBC에서 연결을 뽑아와서 DB 작업을 진행한다. 방식은 Setter로 한다.</p>
<pre><code class="language-xml">&lt;!-- DBCP Bean --&gt;
&lt;bean id=&quot;dataSource&quot; class=&quot;org.apache.commons.dbcp2.BasicDataSource&quot;&gt;
    &lt;property name=&quot;driverClassName&quot; value=&quot;oracle.jdbc.driver.OracleDriver&quot;&gt;&lt;/property&gt;
    &lt;property name=&quot;url&quot; value=&quot;jdbc:oracle:thin:@Localhost:1521:xe&quot;&gt;&lt;/property&gt;
    &lt;property name=&quot;username&quot; value=&quot;id&quot;&gt;&lt;/property&gt;
    &lt;property name=&quot;password&quot; value=&quot;pw&quot;&gt;&lt;/property&gt;
&lt;/bean&gt;

&lt;!-- Spring JDBC Bean --&gt;
&lt;bean id=&quot;jdbcTemplate&quot; class=&quot;org.springframework.jdbc.core.JdbcTemplate&quot;&gt;
    &lt;property name=&quot;dataSource&quot; ref=&quot;dataSource&quot;&gt;&lt;/property&gt;
&lt;/bean&gt;</code></pre>
<p>이제 톰캣이 실행하면서 root-context를 읽어들이면서 두 인스턴스를 만들고 서로 조립해서 메모리에 로딩해둔다. ( DBCP는 참조형이니 ref에 id값을 넣어준다 )</p>
<hr>
<h3 id="4-update">4) update</h3>
<p>Spring JDBC에서는 insert, delete, update의 작업들은 <code>update()</code> 메서드로 한 번에 작업한다. 이때, update메서드에 오버로딩 목록을 보면 <strong>‘가변 인자’</strong>가 존재한다.</p>
<p>이는 개발하는 사람 입장에서 DB에 몇 개의 값을 세팅할지 모르니까, 가변 인자로 넘겨준 것. 그래서 제일 첫 번째 인자로는 ‘SQL문’, 그 뒤로는 <code>?</code> 개수에 맞는 값을 세팅해주면 된다.</p>
<pre><code class="language-java">@Component
public class MessagesDAO {

    @Autowired
    private JdbcTemplate jdbc; // DI로 DBCP를 갖고 있음

    public int insert(MessagesDTO dto) {

        String sql = &quot;insert into messages values(messages_seq.nextval, ?, ?, sysdate)&quot;;
        return jdbc.update(sql, dto.getWriter(), dto.getContents()); // insert, update, delete
    }


    public int update(MessagesDTO dto) {

        String sql = &quot;update Messages set writer=?, content=? where seq=?&quot;;
        return jdbc.update(sql, dto.getWriter(), dto.getContents(), dto.getSeq());

    }


    public int delete(int seq) {

        String sql = &quot;delete from Messages where seq=?&quot;;
        return jdbc.update(sql, seq);
    }    

}</code></pre>
<hr>
<h3 id="5-select">5) select</h3>
<p>select문을 사용하게 되면 난이도가 오른다. 그 이유는 우리가 가져오는 데이터의 자료형이 경우마다 달라지기 때문인데, 일단 한번 만들어보자.</p>
<pre><code class="language-java">public MessagesDTO selectBySeq(int seq) {

    String sql = &quot;select * from messages where seq=?&quot;;

    return jdbc.queryForObject(sql, new RowMapper&lt;MessagesDTO&gt;() {
        @Override
        public MessagesDTO mapRow(ResultSet rs, int rowNum) throws SQLException {

            MessagesDTO dto = new MessagesDTO();
            dto.setSeq(rs.getInt(&quot;seq&quot;));
            dto.setWriter(rs.getString(&quot;writer&quot;));
            dto.setContents(rs.getString(&quot;contents&quot;));
            dto.setWrite_date(rs.getTimestamp(&quot;write_date&quot;));
            return dto;
        }
    }, seq);

</code></pre>
<p>select으로 데이터를 뽑을 때 사용하는 메서드는 <code>queryForObject(), query()</code>라는 메서드들이다. 물론 이외에도 많지만, 이 둘을 주로 사용한다. </p>
<blockquote>
<p>두 메서드의 차이는 가져오는 데이터가 단수인지, 복수인지의 차이이다. </p>
</blockquote>
<ul>
<li><code>queryForObject</code> : int 값 하나 또는 record 한 줄 등의 데이터를 뽑아낼 때.</li>
<li><code>query</code> : 여러 줄의 결과 또는 여러 데이터를 뽑아낼 때 ( 리스트 데이터 )</li>
</ul>
<br>

<p>실제 동작 부분은 쿼리 결과를 ResultSet 인스턴스에 담아서 주는 것 똑같다. 다만 후자는 내부적으로 반복문을 수행하여서, List 형태로 반환해준다.</p>
<p>그리고 둘의 매개변수를 보면 첫째 인자는 ‘SQL문’, 두 번째는 ‘함수’, 세 번째부터는 ‘가변인자’이다. 일단 첫째와 셋째를 보면 당연한 과정이다. SQL문을 넣어주고, 그 값을 세 번째부터 하나씩 <code>?</code>의 개수에 맞춰서 늘어나기 때문이다.</p>
<p>포인트는 쿼리를 넣고, 나오는 결과를 우리가 사용할 수 있도록 데이터를 담는 작업이다. 이걸 기존 jdbc에서는 다음과 같이 진행하였다.</p>
<pre><code class="language-java">List&lt;MessagesDTO&gt; list = new ArrayList&lt;MessagesDTO&gt;();

while(rs.next()) {
    MessagesDTO dto = new MessagesDTO();
    dto.setSeq(rs.getInt(&quot;seq&quot;));
    dto.setWriter(rs.getString(&quot;writer&quot;));
    dto.setContents(rs.getString(&quot;content&quot;));
    dto.setWrite_date(rs.getTimestamp(&quot;write_date&quot;));

    list.add(dto);

}</code></pre>
<p>이런 식으로 우리가 반복을 돌려서 담아왔는데, Spring JDBC를 쓰게 되면 이 작업 또한 jdbc가 대신 처리해준다. 다만 그 방식을 ‘Call-back’으로 함수를 넣어서 전달해야 한다. 왜냐면 스프링이 사용자가 어떤 형식의 DTO 사용할지 모르게 때문이다. </p>
<p>그래서 스프링은 두 번째 변수로 <strong>DTO값을 세팅해줄</strong>, 다시 말하면 값을 담는 방식을 전달해달라고 한다.</p>
<blockquote>
<p>근데 생각해보면 자바에서 콜백이 가능한 일인가?</p>
</blockquote>
<p>실제로 가능하다. 다만 우리가 JS쓰는 것처럼 쉽고 편하지 않다. 애초에 함수 자체를 값으로 쓸 수 없기에, 자바에서는 그 방식을 바꿔야 할 뿐이다.</p>
<p>그리고 그 방식은 아주 간단히 함수를 담은 클래스를 넘기는 것이다. 생각해보자 함수를 넘길 수 없으면, 함수를 담은 클래스를 넘기고 이를 따라 진행하면 된다. 다만 그 기법? 문법을 우리가 알지 못하기 때문에 지금부터 알아보자.</p>
<hr>
<h4 id="rowmapper">RowMapper</h4>
<blockquote>
<p>넘길 클래스는 인터페이스인데, 이걸 <strong>new</strong> 한다. 근데 인터페이스를 new하는 것이 가능한 일인가?</p>
<blockquote>
<p>가능하다. 앞서 말한 것처럼 인터페이스는 오버라이딩이 아직 되지 않아서 new할 수 없을 뿐 오버라이딩만 하면 사용할 수 있다. 그리고 그 방식이 우리가 자바에서 콜백을 사용하는 방법이다.</p>
</blockquote>
</blockquote>
<pre><code class="language-java">new RowMapper&lt;MessagesDTO&gt;() {
        @Override
        public MessagesDTO mapRow(ResultSet rs, int rowNum) throws SQLException {

            MessagesDTO dto = new MessagesDTO();
            dto.setSeq(rs.getInt(&quot;seq&quot;));
            dto.setWriter(rs.getString(&quot;writer&quot;));
            dto.setContents(rs.getString(&quot;contents&quot;));
            dto.setWrite_date(rs.getTimestamp(&quot;write_date&quot;));
            return dto;
        }</code></pre>
<p>그래서 위 코드와 같이 스프링이 DTO를 옮겨담는 방식이 적히 메서드가 mapRow이다. 이 메서드를 @Override 어노테이션으로 명시해준 뒤, 채워서 전달하면 스프링이 ResultSet을 건네받아 DTO를 채워서 반환한다.</p>
<hr>
<h4 id="query">query()</h4>
<p>이와 같이 <code>query()</code> 도 같은 방식으로 동작하지만, 반복의 과정까지 스프링이 해주기 때문에 메서드도 똑같이 건네주면 된다.</p>
<pre><code class="language-java">public List&lt;MessagesDTO&gt; selectAll(){

    String sql = &quot;select * from messages&quot;;

    return jdbc.query(sql, new RowMapper&lt;MessagesDTO&gt;() { 
        @Override
        public MessagesDTO mapRow(ResultSet rs, int rowNum) throws SQLException {

            MessagesDTO dto = new MessagesDTO();
            dto.setSeq(rs.getInt(&quot;seq&quot;));
            dto.setWriter(rs.getString(&quot;writer&quot;));
            dto.setContents(rs.getString(&quot;content&quot;));
            dto.setWrite_date(rs.getTimestamp(&quot;write_date&quot;));
            return dto;
        }
    });
}</code></pre>
<p>추가로 한 개의 숫자같은 데이터를 꺼내올 때는 굳이 콜백을 안쓰고, 반환하는 타입만을 매개변수로 준다.</p>
<pre><code class="language-java">public int selectCount() {
    String sql = &quot;select count(*) from member&quot;;
    return jdbc.queryForObject(sql, Integer.class); 
}</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[Day 79. Spring Framwork 3 : 게시판 CRUD 구현]]></title>
            <link>https://velog.io/@ho_c/%EA%B5%AD%EB%B9%84%EA%B5%90%EC%9C%A1-Day-79</link>
            <guid>https://velog.io/@ho_c/%EA%B5%AD%EB%B9%84%EA%B5%90%EC%9C%A1-Day-79</guid>
            <pubDate>Sun, 12 Jun 2022 11:35:40 GMT</pubDate>
            <description><![CDATA[<p>오늘부턴 JSP, Servlet으로만 만들었던 게시판 CRUD를 Spring으로 구현할 것이다. 하지만 이보다 앞서 CRUD를 Spring으로 구현하는 기본 로직을 연습해보고, 게시판 기능을 구현할 것이다. </p>
<hr>
<h2 id="crud-연습">CRUD 연습</h2>
<h3 id="1-create">1. CREATE</h3>
<p>가장 먼저 게시판에 글을 입력하는 로직을 만들어보자.</p>
<h4 id="1-ojdbc-dbcp-라이브러리-추가-spring-전용-jdbc가-따로-있음">1) ojdbc, dbcp 라이브러리 추가 (Spring 전용 jdbc가 따로 있음)</h4>
<p>일단 데이터를 저장하기 위해, DB 연결을 만들어줄 ojdbc라이브러리를 Maven으로 받아오자.</p>
<ul>
<li>Maven repo에서 “ojdbc8”, “dbcp” 검색</li>
<li>pom.xml에 추가</li>
</ul>
<p>라이브러리가 만들어졌다면, 입력 로직을 실행할 DAO를 만들어 준다. DAO 클래스까지 만들었다면, 이제 new를 이용해서 Controller에서 DAO 인스턴스를 사용해야 할 것 같지만, 스프링 프레임워크에서는 더 그러지 않는다. (의존성 때문에)</p>
<pre><code class="language-java">@RequestMapping(&quot;inputProc&quot;)
public String inputProc(MessagesDTO dto {

    MessagesDAO dao = new MessagesDAO();

    return &quot;redirect:/&quot;;
    }
</code></pre>
<p>지난번에 배운 것처럼 <code>root-context.xml</code>이나, <code>servlet-context.xml</code> 같은 xml에 문서에 가서 DAO bean을 만들 수도 있지만, 이외에도 Spring에서 빈을 만드는 방식은 더 있기 때문에 이를 알아보자.</p>
<hr>
<h4 id="2-bean-생성방식">2) bean 생성방식</h4>
<p>먼저 스프링에서 빈을 생성하는 방식은 총 3가지이다. 이중 XML과 Annotation만 알아보자.</p>
<p>*<em>(1) XML *</em></p>
<p>XML로 인스턴스를 생성하는 방식은 지난번에 배운 <code>&lt;bean&gt;</code> 태그를 사용한다.</p>
<pre><code class="language-xml">&lt;beans:bean class=&quot;org.springframework.web.servlet.view.InternalResourceViewResolver&quot;&gt;
    &lt;beans:property name=&quot;prefix&quot; value=&quot;/WEB-INF/views/&quot; /&gt;
    &lt;beans:property name=&quot;suffix&quot; value=&quot;.jsp&quot; /&gt;
&lt;/beans:bean&gt;</code></pre>
<p><strong>(2) Annotation</strong></p>
<p>어노테이션으로 빈 생성의 핵심은 <code>&lt;context:component-scan&gt;</code>라는 xml태그이다. 이 태그를 넣어두면 <code>@Component</code>가 적용된 클래스를 찾는다. 그리고 해당 스크립트가 읽히게 될 때, 인스턴스로 메모리 상 Spring Container(pool)에 로딩해둔다.</p>
<p>결과적으로 <code>&lt;context:component-scan&gt;</code>는 <code>&lt;bean&gt;</code>와 같은 역할을 한다. 다만 bean이 class 속성으로 해당 인스턴스의 경로를 끌어왔다면, component-scan은 <code>base-package</code> 속성으로 그 탐색 범위를 지정해줘야 한다.</p>
<p>그래서 Spring 부팅 순서에서 추가된 것은 클라이언트의 요청이 들어오면, Dispatcher가 만들어지면서, 핸들러 매핑-View Resolver 인스턴스-Component 인스턴스 생성 및 Spring Container에 로딩된다.</p>
<h4 id="어노테이션으로-하는-di--autowired">어노테이션으로 하는 DI : @Autowired</h4>
<p>이 경우 한 가지 직면하는 문제가 있다. 어노테이션으로 bean으로 만들 경우, Component를 포함 상속받는 어노테이션 적용 대상들이 메모리에 로딩됐다. 그러면 둘 다 메모리에 올라와 있는 상태에서 DI를 적용하기 어려워진다.</p>
<p>즉, XML을 사용하면 문서를 읽어드리는 과정에서 스프링이 빈을 생성하고 DI한다. 이때 문제는 어노테이션으로 하면 이미 각각의 인스턴스들이 적재되어 있고, 이 둘의 의존관계를 만들기 어렵다는 것이다.</p>
<p>하지만 이 고민도 잠시, 어노테이션으로 빈을 만들면 DI도 어노테이션으로 해주면 된다.
그 방법이 <code>@Autowired</code>이다.</p>
<p><code>component-scan</code>이 실행이 되면 스프링 풀에 인스턴스들이 적재된 상태가 되는 동시에, 내부에 <code>@Autowired</code>가 자료형을 바탕으로 적용된 인스턴스를 끌어와서 해당 변수에 넣어줘서 DI한다.</p>
<h4 id="qulifierid">@Qulifier(ID)</h4>
<p>이 외에 같은 자료형의 인스턴스를 넣고 싶다면 @Component를 넣어줄 때, <code>@Component(ID)</code>로 id를 설정해서, @Autowired 밑에 @Qulifier(ID)를 설정해서 불러줄 수 있다.</p>
<hr>
<h4 id="3-db-연결-만들기-dao">3) DB 연결 만들기 (DAO)</h4>
<p>연결을 만들어내기 위해서 <code>DataSource</code> 클래스를 이용할 것인데, 여기서도 new를 사용하지 않는다. 근데 문제는 DataSource는 우리 소유, 즉 우리가 가진 소스코드가 아닌 외부에서 만든 클래스라 XML로 빈을 만들어줘야 한다.</p>
<ul>
<li>root-context로 이동
해당 XML은 외부 라이브러리 세팅과 관련된 설정을 하는 문서이다. 그래서 우리는 톰캣 서버가 구동되면서 Spring 컨테이너 안에다가 DBCP를 로딩해놓을 것이다.</li>
</ul>
<p>방식은 앞서 말한 것처럼 어노테이션을 사용할 수 없으니 XML로 해주고, 빈 생성은 기존 DBCP처럼 Setter를 활용한다.</p>
<pre><code class="language-xml">&lt;bean class=&quot;org.apache.commons.dbcp2.BasicDataSource&quot;&gt;
    &lt;property name=&quot;driverClassName&quot; value=&quot;oracle.jdbc.driver.OracleDriver&quot;&gt;&lt;/property&gt;
    &lt;property name=&quot;url&quot; value=&quot;jdbc:oracle:thin:@Localhost:1521:xe&quot;&gt;&lt;/property&gt;
    &lt;property name=&quot;username&quot; value=&quot;kh&quot;&gt;&lt;/property&gt;
    &lt;property name=&quot;password&quot; value=&quot;kh&quot;&gt;&lt;/property&gt;
&lt;/bean&gt;</code></pre>
<blockquote>
<p><strong>이 과정 어딘가 익숙하지 않나?</strong></p>
</blockquote>
<p>바로 톰캣 환경에서 사용했던 JNDI 방식이다. 우리가 톰캣에서 JNDI로 했던 것을 이제 스프링에서는 이렇게 대체가 된다. 그리고 저 코드는 톰캣이 실행되면서 DBCP 인스턴스를 스프링 컨테이너에 적재해놨기 때문에, MessagesDAO에서 @Autowired를 통해서 DI를 해줄 수 있다.</p>
<pre><code class="language-java">public class MessagesDAO {
    @Autowired
    private DataSource bds;
}</code></pre>
<h4 id="4-dao-insert-생성-및-controller에서-적용">4) DAO insert() 생성 및 Controller에서 적용</h4>
<p><strong>[ MessagesDAO ]</strong></p>
<pre><code class="language-java">public int insert(MessagesDTO dto) throws Exception{

    String sql = &quot;insert into messages values(messages_seq.nextval, ?, ?, sysdate)&quot;;

    try(
        Connection con = bds.getConnection();
        PreparedStatement pstat = con.prepareStatement(sql);
        ){
    pstat.setString(1, dto.getWriter());
    pstat.setString(2, dto.getContents());
    return pstat.executeUpdate();
    }
}
</code></pre>
<p><strong>[ Controller ]</strong></p>
<pre><code class="language-java">try {
    dao.insert(dto);
} catch (Exception e) {
    return &quot;error&quot;;
}
</code></pre>
<hr>
<h3 id="2-read">2. READ</h3>
<p>기본 과정은 데이터 입력과 똑같다. 출력 메서드를 쓰다 보면 데이터를 담을 List 컬렉션과 DTO를 만드려면 <code>new</code>를 사용한다.</p>
<p>그래서 의존성 문제에 대해서 고민이 생길 수 있는데, 어차피 new을 아예 사용을 안하는 건 불가능하다. 그리고 다음 코드처럼 단발성으로 지역변수 형태로 사용하는 객체나, 데이터를 담는 DTO을 bean으로 사용하면, Spring 컨테이너가 불필요한 메모리 자원을 너무 많이 만들게 된다.</p>
<h4 id="1-데이터-불러오기">1) 데이터 불러오기</h4>
<pre><code class="language-java">public List&lt;MessagesDTO&gt; selectAll() throws Exception{

    String sql = &quot;select * from messages&quot;;

    try(
            Connection con = bds.getConnection();
            PreparedStatement pstat = con.prepareStatement(sql);
            ResultSet rs = pstat.executeQuery();
            ){
        // 단발성 데이터 : 지역변수    
        List&lt;MessagesDTO&gt; list = new ArrayList&lt;MessagesDTO&gt;();

        while(rs.next()) {
            MessagesDTO dto = new MessagesDTO();
            dto.setSeq(rs.getInt(&quot;seq&quot;));
            dto.setWriter(rs.getString(&quot;writer&quot;));
            dto.setContents(rs.getString(&quot;contents&quot;));
            dto.setWrite_date(rs.getTimestamp(&quot;write_date&quot;));                

            list.add(dto);

        }

        return list;
    }</code></pre>
<hr>
<h4 id="2-데이터-넘기기">2) 데이터 넘기기</h4>
<p>데이터를 넘기는 방법은 2가지인 것도 마찬가지이다. request에 담거나, session에 담거나. 그러나 굳이 session에 영구적으로 담았다가 지울 필요가 없다. 그래서 request방식을 택할 껀데, 이때 스프링에서는 아래 코드처럼 request 객체를 사용할 필요가 없다.</p>
<pre><code class="language-java">@RequestMapping(&quot;toOutput&quot;)
public String toOutput(HttpServletRequest request) {

    try {
        List&lt;MessagesDTO&gt; list = dao.selectAll();
        request.setAttribute(&quot;list&quot;, list);
        request.getRequestDispatcher(null);

    } catch (Exception e) {
        e.printStackTrace();
        return &quot;error&quot;;
    }

    return &quot;output&quot;; // 기본 방식이 forward이다.
}</code></pre>
<p>스프링 프레임워크는 DB에서 꺼내서 넘기는 데이터를 ‘Model data’라고 부르고, 이를 관리해주는 클래스로서 <code>Model</code>을 제공한다. 그래서 쉽고 편하게 Model 인스턴스에다가 넣어두면 View에서 사용할 수 있다.</p>
<pre><code class="language-java">@RequestMapping(&quot;toOutput&quot;)
public String toOutput(Model model) {

    try {
        List&lt;MessagesDTO&gt; list = dao.selectAll();
        model.addAttribute(&quot;list&quot;, list);    

    } catch (Exception e) {
        e.printStackTrace();
        return &quot;error&quot;;
    }

    return &quot;output&quot;; // 기본 방식이 forward이다.
}</code></pre>
<hr>
<h3 id="3-delete">3. DELETE</h3>
<p>데이터 삭제하기는 앞의 두 개보다 훨씬 쉽고 편하다. 삭제할 시퀀스 값을 전달받아서 DB에서 삭제한 뒤, <code>redirect:toOutput</code>으로 uri를 비우면서 재출력 시키면 된다.</p>
<pre><code class="language-java">@RequestMapping(&quot;deleteProc&quot;)
public String deleteProc(int seq) {

    try {

        dao.delete(seq);

    }catch (Exception e) {
        e.printStackTrace();
        return &quot;error&quot;;
    }

    return &quot;redirect:toOutput&quot;;

}</code></pre>
<hr>
<h3 id="4-update">4. Update</h3>
<p>드디어 마지막 수정이다. 수정 역시도 별반 다를게 없기에 DAO, Controller 코드만 작성하겠다.</p>
<p><strong>[ Controller ]</strong></p>
<pre><code class="language-java">@RequestMapping(&quot;modifyProc&quot;)
public String modifyProc(MessagesDTO dto) throws Exception{

        dao.update(dto);

    return &quot;redirect:toOutput&quot;;
}</code></pre>
<p>**[ DAO ]
**</p>
<pre><code class="language-java">public int update(MessagesDTO dto) throws Exception{

    String sql = &quot;update Messages set writer=?, content=? where seq=?&quot;;

    try(
            Connection con = bds.getConnection();
            PreparedStatement pstat = con.prepareStatement(sql);
            ){
        pstat.setString(1, dto.getWriter());
        pstat.setString(2, dto.getContents());
        pstat.setInt(3, dto.getSeq());

        return pstat.executeUpdate();
    }
}</code></pre>
<hr>
<h3 id="5-exceptionhandler">5. @ExceptionHandler</h3>
<p>앞선 메서드에선 <code>try-catch</code>을 통해 예외를 처리해줬다. 이와 달리 스프링에선 어노테이션을 통해서 처리한다. 먼저 기본에 있던 <code>try-catch</code>는 모두 없애자.</p>
<pre><code class="language-java">@RequestMapping(&quot;deleteProc&quot;)
public String deleteProc(int seq) {

        dao.delete(seq);    

    return &quot;redirect:toOutput&quot;;
}</code></pre>
<p>그리고 하단에 <strong>ExceptionHandler</strong> 어노테이션을 정의해주자.</p>
<pre><code class="language-java">@ExceptionHandler
public String execeptionHandler(Exception e) {
    e.printStackTrace();
    return &quot;error&quot;;

}</code></pre>
<p>이렇게 정의를 해주면, 메서드들이 실행되고 리턴으로 넘어가기 전에 <code>ExceptionHandler</code>를 거치게 된다. 이 과정에서 예외가 발생하면 정의된 것처럼 <code>error.jsp</code>로 넘어가게 된다.</p>
<p>그래서 모든 예외를 인식할 수 있도록 <code>Exception e</code>를 인자값으로 넣어준다. 더 나아가 디테일한 예외처리도 어노테이션을 통해서 정의할 수 있다.</p>
<pre><code class="language-java">@ExceptionHandler(“NumberFormatException”)
public String execeptionHandler(Exception e) {
    e.printStackTrace();
    return &quot;error&quot;;

}</code></pre>
<p>이렇게 정의해주면, 숫자형식에서 발생하는 예외들만 해당 메서드에서 인지해서 처리하게 된다.</p>
<hr>
<h2 id="게시판-만들기">게시판 만들기</h2>
<p>스프링 프레임워크를 이용한 기본 CRUD 구현법을 이해했다면, 이제 이를 토대로 게시판을 한번 더 만들자.</p>
<h3 id="1-회원가입-페이지-매핑">1. 회원가입 페이지 매핑</h3>
<blockquote>
<h4 id="point-매핑-시-검색-효율-높이기">Point. 매핑 시, 검색 효율 높이기</h4>
</blockquote>
<p>첫 번째는 회원가입 페이지로 이동하는 단순 API를 만들 것이다. 여기서 고려할 것은 세미 땐 프론트에서 바로 JSP 경로 입력으로 끝났지만, Spring에선 DS를 거쳐야 해서 매핑을 해줘야 한다.</p>
<p>이때, 매핑은 <code>@Controller</code> <code>@RequestMapping</code> 어노테이션으로 만들어 요청되는 엔드포인트를 분석한다. 하지만 프로젝트가 확장되어 매핑 요소들이 늘어나면 그만큼 매핑 목록도 늘어나 검색 비효율을 일으킨다. 이 때문에 다음처럼 <code>@Controller</code> 자체를 분류시킨다.</p>
<p><strong>[ JSP ]</strong></p>
<pre><code class="language-javascript">$(&quot;#join&quot;).on(&quot;click&quot;, function(){
    location.href=&quot;/member/toJoin&quot;;                
});</code></pre>
<p><strong>[ Controller ]</strong></p>
<pre><code class="language-java">@Controller
@RequestMapping(&quot;/member/&quot;) // member파트는 모두 여기 있다.
public class MemberController {

    @RequestMapping(&quot;toJoin&quot;)
    public String toJoin() throws Exception{

        return &quot;/member/join&quot;;
    }
}</code></pre>
<hr>
<h3 id="2-아이디-중복체크-기능">2. 아이디 중복체크 기능</h3>
<blockquote>
<h4 id="point-ajax반환에는-return을-사용하지-않는다-그리고-ajax세팅이-필요하다">Point. ajax반환에는 <code>return</code>을 사용하지 않는다. 그리고 ajax세팅이 필요하다.</h4>
</blockquote>
<p>이번에는 회원가입 시, 아이디 중복체크 기능을 만들어보면서 Spring에서 Ajax를 사용하는 방법을 알아보자.</p>
<p>일단 프론트에 작성 Ajax는 다음과 같고, 반환되어 오는 String은 따로 사용하지 않고 콘솔 출력되도록 설정했다.</p>
<pre><code class="language-javascript">$(&quot;#id&quot;).on(&quot;blur&quot;, function(){
    $.ajax({
        url:&quot;/member/idDuplCheck&quot;,
        data: {&quot;id&quot;:$(&quot;#id&quot;).val()}
    }).done(function(resp){
        console.log(resp);
    });
});</code></pre>
<p>위 코드에 의해서 id 컴포넌트의 초점이 상실되면 ajax가 실행되어, 아래 나오는 <code>/member/</code> 컨트롤러 휘하의 <code>idDuplCheck</code>가 실행된다. 그러면 해당 메서드는 DB에서 DAO를 통해서 조회를 실행한 뒤, 그 값을 반환한다.</p>
<pre><code class="language-java">@ResponseBody
@RequestMapping(value=&quot;idDuplCheck&quot;, produces = &quot;text/html; charset=utf8&quot;) // Ajax세팅
public String idDuplCheck(String id) throws Exception {

    if(dao.idCheck(id)) {
        return &quot;사용 불가능&quot;;
    } else {
        return &quot;사용 가능&quot;;
    }
}</code></pre>
<p>하지만 위 코드를 보면 <code>return</code>이 존재하지 않는다. 배운 거랑 조금 문제가 있다. 그럼 일단 생각해보자.</p>
<p>컨트롤러에서 DS에 보내는 응답은 2가지 설정을 통해서 ViewResolver에서 사용해서 jsp로 View만들어 반환하는 것이었다. 이 말은 곧 페이지 간의 이동이 발생한다는 것이다.</p>
<p>그러나 Ajax에 대한 응답은 페이지 이동이 불필요하다. 애초에 이 기술 자체가 페이지 이동 없는 데이터 교환이 목적인데 말이다. 따라서 forward도 아니고, redirect도 아니다. 그냥 단순한 String만 클라이언트에 보내면 된다.</p>
<p>이를 위해서, 해당 메서드가 ajax를 사용하기 위한 메서드임을 알려주는 어노테이션이 <code>RespomseBody</code>이다. 이걸 위에 붙여주게 되면 DS가 우리한테 받은 값 그대로 클라이언트한테 String으로 보내준다.</p>
<p>다만 한글인 경우 브라우저의 ajax가 이를 분석해줄 수 없다. 따라서 매핑 안에 인자값을 줘서 ajax한테 해당 데이터가 어떻게 설정되어 있는지를 알려준다. 추가로 <code>RequestMapping</code> 은 기본으로 uri만 인자값으로 받았지만, 위와 같은 형태로 추가적인 설정이 가능하다. (전달방식, 세팅 등등)</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Day 63. Git 2 : Remote Git]]></title>
            <link>https://velog.io/@ho_c/%EA%B5%AD%EB%B9%84%EA%B5%90%EC%9C%A1-Day-63</link>
            <guid>https://velog.io/@ho_c/%EA%B5%AD%EB%B9%84%EA%B5%90%EC%9C%A1-Day-63</guid>
            <pubDate>Thu, 09 Jun 2022 11:51:02 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>세미 전에 원격 깃 배워놓고, 프로젝트가 바로 시작하는 바람에 정리하지 못했다. 그러니 오늘은 Github를 통한 Remote Git를 정리해봤다.</p>
</blockquote>
<p>일단, Remote Git과 Local의 차이는 저장 공간의 규모가 더 커진다는 것이다.</p>
<ul>
<li>Local [ Working Tree → Stage → L-R ] : .git 폴더가 “내 하드웨어(컴퓨터)”에만 존재</li>
<li>Remote [ Working Tree → Stage → L-R(.git) → Git Server(R-R)] </li>
</ul>
<p>이로 인해 우리가 저장한 데이터는 로컬과 리모트에 원본이 각각 저장되고 이를 “분산처리”라고 한다.   </p>
<hr>
<h2 id="github-remote-server">Github (Remote Server)</h2>
<h3 id="1-remote-repo-만들기">1) Remote repo 만들기</h3>
<ul>
<li><p>Pubilc : 모두가 볼 수 있고, contribution이 있어 다른 이들이 참여할 수 있다.</p>
</li>
<li><p>contribution은 오픈소스 프로젝트라서, 우리가 기여하면 (주인장 승인) 일종의 스펙이 된다.</p>
</li>
<li><p>Private : 비공개</p>
</li>
<li><p>Add a README file, Add .gitignore : 직접 처리</p>
</li>
<li><p>Choose a license : 오픈소스 생성에 기여할 라이센스 방식들을 선택</p>
</li>
</ul>
<h3 id="2-로컬에-올릴-폴더-만들기-→-로컬-깃">2) 로컬에 올릴 폴더 만들기 → 로컬 깃</h3>
<h3 id="3-로컬의-기록을-리모트로-올리기">3) 로컬의 기록을 리모트로 올리기</h3>
<ul>
<li>git remote –v : 저장된 원격 주소지 출력</li>
<li>git remote add origin &quot;깃허브 레포 url&quot;: 원격 저장소 주소를 깃에 저장</li>
</ul>
<p><strong>*origin : 관습적으로 정하는 리모트 저장소 이름</strong></p>
<p>현재 깃허브 레포는 오픈 소스(public)으로 해둬서 다운로드는 자유롭다. 그러나 업로드는 제한이 되어 있다.</p>
<h3 id="3-1-업로드-push--다운로드-pull">3-1) 업로드 (push) / 다운로드 (pull)</h3>
<ul>
<li>git push &lt;대상 리모트 명&gt; &lt;브랜치명&gt;
업로드는 브랜치 단위로 진행된다. </li>
</ul>
<p>결과적으로 데이터 교류는 로컬과 리모트 사이에서만 일어나며, 워킹 트리는 체크 아웃으로 브랜치만 변경하는 것이다.</p>
<hr>
<h2 id="team-play">Team Play</h2>
<blockquote>
<p>깃 허브로 협업을 하는 방식은 2가지가 있다.</p>
</blockquote>
<h4 id="1-collaborate--가장-일반적인-방식">1) Collaborate : 가장 일반적인 방식</h4>
<p>담당자가 리모트 공유 메일을 조원들에게 보내고, 참여하게 되면 모든 공유자가 해당 리모트에 대한 주인이 된다. </p>
<p>즉, 모두가 push, pull이 가능해지는데 이 경우 멀티 스레드에 의한 동시성 문제가 있기 때문에 신나는 충돌 파티가 벌어진다. 그래서 익숙하지 않은 초급자들에겐 추천되지 않는다.</p>
<h4 id="2-pull-request-contribution과-동일함">2) Pull Request (Contribution)과 동일함.</h4>
<p><strong>한 명의 리모드 주인 – 다수의 참여자</strong></p>
<p>먼저 풀리퀘스트는 담당자가 리모트를 개설하면서 시작된다.
그러면 참여자들은 담당자의 리모트를 자신의 리모트로 끌고 오는데, 이를 fork라 한다. 
그 다음, 복사본을 자신의 로컬로 가져온다.</p>
<p>이 경우, 담당자만 push가 가능하며, 다른 참여자들은 PR을 담당자에게 신청하여 본인이 로컬에서 작업한 내용을 담당자의 원격 리모트에 반영한다.</p>
<p>포인트는 ‘버전을 올라갔다면 조원은 자신의 리모트 저장소로 푸쉬해야 한다’이다.</p>
<p>솔직히 세미 3주동안 하면서 느낀 건, 담당자 시간에 PR신청을 맞추는 것이 아니라 담당자가 참여자들의 시간에 맞춰야지 프로젝트가 능동적으로 돌아갔다.</p>
<p>한가지 문제점은 그럼 버전이 올라가면, 기존에 작업하던 내용은 어떻게 될 것인가이다.
그때 내가 썻던 방법은 PR이 승인되면 모두가 하던 작업을 “commit”한다. 물론 각자의 fork 레포에는 올리지 않는다.</p>
<p>이렇게 되면, 자동으로 커밋 내용과 pull한 내용이 겹쳐져서 작업내용이 안 날라간다. 추후 작업이 완료 되면 fork.레포에 push 후 pr을 신청한다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Day 77. Spring Framwork 2 : DI, 스프링MVC 기본]]></title>
            <link>https://velog.io/@ho_c/%EA%B5%AD%EB%B9%84%EA%B5%90%EC%9C%A1-Day-77</link>
            <guid>https://velog.io/@ho_c/%EA%B5%AD%EB%B9%84%EA%B5%90%EC%9C%A1-Day-77</guid>
            <pubDate>Thu, 09 Jun 2022 10:54:16 GMT</pubDate>
            <description><![CDATA[<p>지난 시간에 이어, 스프링 프레임워크의 핵심들을 알아보자.</p>
<h2 id="1-di--의존성-주입">1. DI : 의존성 주입</h2>
<p>이제 두 객체 간의 의존관계도 직접 DL을 하거나, new로 생성을 하지 않는다. 결과적으로 세팅은 개발하지만 전반적인 과정은 스프링이 진행한다. </p>
<p>일단 DI로 객체 간의 의존관계를 만드는 방식은 2가지이다.</p>
<p>*<em>① 생성자를 사용
② Setter를 사용
*</em></p>
<p>이제 이를 직접 코드상에 명시하는 것이 아닌, xml 설정 파일을 통해서 스프링 컨테이너 생성 시 자동으로 만들어지게 한다.</p>
<p>먼저 다음의 두 클래스가 있다.</p>
<p><strong>[ SamsungTV.class ]</strong></p>
<pre><code class="language-java">public class SamsungTV implements TV{

    private Speaker speaker ;

    public SamsungTV() {
    }


    public SamsungTV(Speaker speaker) {
        System.out.println(&quot;삼성TV가 생성되었음&quot;);
        this.speaker = speaker;
    }

    public void powerOn() {}

    public void powerOff() {}



    public Speaker getSpeaker() {
        return speaker;
    }

    public void setSpeaker(Speaker speaker) {
        this.speaker = speaker;
    }

}

</code></pre>
<p><strong>[ AppleSpeaker.class ]</strong></p>
<pre><code class="language-java">public class AppleSpeaker implements Speaker{

    public AppleSpeaker() {
        System.out.println(&quot;Apple Speaker Setting Complete&quot;);
    }

    public void volumUp() {
    }

    public void volumDown() {
    }

}</code></pre>
<p>일단 두 클래스 모두 각자의 인터페이스를 가지고 있고, 삼성TV 클래스는 Apple 스피커 클래스를 내부에서 사용할 것이다. 즉, 삼성TV가 애플 스피커를 의존한다.</p>
<p>따라서 이 의존관계를 앞선 2가지, Setter와 생성자를 통해서 만들어줄 수 있다. 물론 DL을 사용해도 되지만, 더 깔끔한 방법인 DI를 권장하며, new 생성자는 결합도을 직접 꽂아버리는 것이기 때문에 사용하지 않는다.</p>
<p>이제 결합을 해보자. 결합 자체는 코드 상이 아닌, XML파일을 통해서 진행한다. 나의 경우 스프링 컨테이너가 분석할 파일의 이름을 <code>context.xml</code>로 설정하였다.</p>
<p><strong>[ context.xml ]</strong> : 빈 생성</p>
<pre><code class="language-xml">&lt;bean id=&quot;tv&quot; class=&quot;kh.spring.tv.SamsungTV&quot;&gt;&lt;/bean&gt;
&lt;!-- TV tv = new SamsungTV(); --&gt;

&lt;bean id=&quot;speaker&quot; class=&quot;kh.spring.speaker.AppleSpeaker&quot;&gt;&lt;/bean&gt;
&lt;!-- Speaker speaker = new AppleSpeaker(); --&gt;</code></pre>
<hr>
<h3 id="1-생성자-사용">1) 생성자 사용</h3>
<p>앞선 DL에서 배웠듯이, <code>&lt;bean&gt;</code> 태그는 스프링 컨테이너가 메모리에 생성되면서 생성할 클래스를 명시한다. 그래서 아래 코드는 2개의 인스턴스를 생성한다.</p>
<p>이 과정에서 삼성TV 인스턴스가 생성되면서, 그 때 <code>Speaker speaker</code>라는 참조변수를 매개변수로 전달해야 한다. 이를 위해서 사용하는 태그가 바로 <code>&lt;constructor-arg&gt;</code>이다.</p>
<p><strong>[ context.xml ]</strong></p>
<pre><code class="language-xml">&lt;bean id=&quot;tv&quot; class=&quot;kh.spring.tv.SamsungTV&quot;&gt;
    &lt;constructor-arg ref=&quot;speaker&quot;&gt;&lt;/constructor-arg&gt;
    &lt;!-- new SamsungTV(speaker) --&gt;
&lt;/bean&gt;
&lt;!-- TV tv = new SamsungTV(); --&gt;

&lt;bean id=&quot;speaker&quot; class=&quot;kh.spring.speaker.AppleSpeaker&quot;&gt;&lt;/bean&gt;
&lt;!-- Speaker speaker = new AppleSpeaker(); --&gt;</code></pre>
<p>결과적으로 위와 같이 작성할 수 있다. </p>
<p>추가로 <code>&lt;constructor-arg&gt;</code>는 <code>&lt;bean&gt;</code> 태그 안에 사용할 수 있는 내부 태그로 2가지 속성이 있다.</p>
<ul>
<li><code>value</code> : 매개변수의 자료형이 기본형 int, String, long, boolen 같은 경우 사용</li>
<li><code>ref</code> : 참조변수일 경우</li>
</ul>
<p>그리고 그 값으로 전달할 bean의 id 속성 값을 넣어주면 된다.</p>
<p>번외로 삼성TV에 인스턴스 변수와 생성자를 추가하면 다음도 가능하다.</p>
<pre><code class="language-java">private int price;

public SamsungTV(Speaker speaker, int price) {
    System.out.println(&quot;삼성TV가 생성되었음&quot;);
    this.speaker = speaker;
    this.price = price;
}</code></pre>
<pre><code class="language-xml">&lt;bean id=&quot;tv&quot; class=&quot;kh.spring.tv.SamsungTV&quot;&gt;
    &lt;constructor-arg ref=&quot;speaker&quot;/&gt;
    &lt;constructor-arg value=&quot;1000000&quot;/&gt;
    &lt;!-- new SamsungTV(speaker, price) --&gt;
&lt;/bean&gt;</code></pre>
<hr>
<h3 id="2-setter-사용">2) Setter 사용</h3>
<p>Setter의 경우에는 <code>&lt;property&gt;</code>라는 내부 태그를 사용하고, 생성자의 경우와 마찬가지로 속성이 존재한다.</p>
<ul>
<li><code>name</code> : 세팅할 변수명 → setSpeaker, setPrice 가 호출된다.</li>
<li><code>ref</code> : 참조변수일 경우 사용</li>
<li><code>value</code> : 기본형일 경우 사용</li>
</ul>
<p>따라서 xml에서는 다음과 같이 사용한다.</p>
<pre><code class="language-xml">&lt;bean id=&quot;tv&quot; class=&quot;kh.spring.tv.SamsungTV&quot;&gt;
    &lt;!-- Setter --&gt;
    &lt;property name=&quot;speaker&quot; ref=&quot;apple&quot;/&gt;
    &lt;property name=&quot;price&quot; value=&quot;1000000&quot;/&gt;
&lt;/bean&gt;</code></pre>
<hr>
<h2 id="2-spring-mvc">2. Spring MVC</h2>
<p>이제 웹 프로젝트로 넘어가보자. 그 전에 Spring을 이용해서 웹 프로젝트를 하려면 다음 2가지를 알아야 한다.</p>
<ul>
<li><strong>Spring MVC</strong></li>
<li><strong>Spring 부팅 순서</strong></li>
</ul>
<blockquote>
<p>Spring MVC를 보기 전 간단한 요약으로 MVC1부터 쭉 살펴보자.</p>
</blockquote>
<h3 id="mvc1">MVC1</h3>
<p><strong>[ 구조 ]</strong>
<strong>Client(JSP) → Tomcat → JSP( Control) → DAO (Model) → DB → DAO → JSP ( Control ) → JSP (View)</strong></p>
<p>MVC1은 프론트와 백엔드가 한 JSP에 묶여있어, 애초에 코드 분류가 안되기 때문에 최소한의 유지보수가 불가능했다. 그래서 나온 게, 지난 세미 내내 사용했던 MVC2이다.</p>
<hr>
<h3 id="mvc2">MVC2</h3>
<p><strong>[ 구조 ]</strong>
<strong>Client → Tomcat → Servlet(Controller) → DAO(Model) → DB → DAO → Servlet → JSP (View)</strong></p>
<p>MVC2의 경우, 프론트와 백엔드가 JSP와 서블릿으로 구분된다. 그래서 사용자의 요청에 대해 톰캣이 맵핑으로 요청을 전달하고 서블릿 컨트롤러를 거쳐 DAO로 넘어가 DB 작업을 처리한다.</p>
<p>DB작업 결과는 컨트롤러가 JSP에 보내서 EL/JSTL을 통해서 View를 채운 뒤, 클라이언트에게 반환한다. 결과적으로 MVC2의 포인트는 프론트, 백엔드, DB의 구분이 확실하게 이뤄져서 유지 보수가 가능하다는 것이다.</p>
<p>하지만 MVC2의 경우도 의존성 문제가 존재하고, 코드 중복도(맵핑 시, 조건문 반복)와 유지 보수의 한계가 있었다. 이를 해결할 수 있는게 바로 ‘Spring MVC’이다.</p>
<hr>
<h3 id="spring-mvc">Spring MVC</h3>
<p><strong>[ 구조 ]</strong>
<strong>Client(URL) → Tomcat → DS (Front Controller) → HM → DS → HandlerAdapter → Controller (service) → DAO → DB → DAO → (Service) → HandlerAdapter → DS → View Resolver(객체) → JSP</strong></p>
<blockquote>
<ul>
<li>DS : Dispatcher Servlet</li>
</ul>
</blockquote>
<ul>
<li>HM : HandlerMapping</li>
<li>HA : HandlerAdapter</li>
</ul>
<h4 id="1-tomcat">(1) Tomcat</h4>
<p>스프링이 추구하는 POJO는 상속을 지양하여 서블릿을 거의 사용하지 않는다. 다만 사용자가 URL을 통해 요청하게 되면, WAS인 Tomcat으로 넘어가게 된다.</p>
<h4 id="2-dispatcher-servlet">(2) Dispatcher Servlet</h4>
<p>이후 톰캣은 요청을 서블릿으로 보내는데, 이 서블릿은 우리 아닌 스프링이 만드는 서블릿이며, <code>Dispatcher Servlet</code>이라고 한다.</p>
<ul>
<li>Spring MVC에서 진정한 의미의 프론트 컨트롤러</li>
<li>‘흐름 제어’가 중심 기능이다.</li>
<li>요청이 들어오는 순간에 생성된다.</li>
<li>사용자 요청의 URL을 처리하기 위해, Handler Mapper 인스턴스를 통해 URL과 매칭되는 컨트롤러를 탐색한다.</li>
</ul>
<h4 id="3-handlermapping">(3) HandlerMapping</h4>
<p>Handler Mapper는 각 URL에 연결된 Controller의 정보를 담고 있다. 그래서 URL을 확인한 뒤, 적합한 Controller를 다시 Dispatcher한테 전달한다.</p>
<ul>
<li>URL과 Controller의 정보를 담고 있다.</li>
<li><code>@Controller</code>을 대상으로 탐색한다.</li>
</ul>
<h4 id="4-handleradapter---controller">(4) HandlerAdapter - Controller</h4>
<p>그러면 Dispatcher는 HandlerAdapter를 통해 처리를 위임하고, 선택된 컨트롤러가 로직을 실행한 뒤 결과를 리턴하면 다시, HA가 <code>ModelAndView</code> 객체에 담아서 DS에 리턴한다. </p>
<h4 id="5-viewresolver">(5) ViewResolver</h4>
<p>호출의 결과를 사용자에게 보여주기 위해선, View를 선택해야 한다. 컨트롤러로부터 반환받는 값에 미리 세팅해둔 접두사와 접미사를 연결시켜서 ‘경로’를 만들어낸다.</p>
<ul>
<li><code>DispatcherServlet</code> 인스턴스가 만들어지면서, 함께 만들어진다. (요청이 있을 때)</li>
<li><code>return</code> 으로 경로 코드를 쉽게 처리할 수 있다.</li>
<li>경로 설정에 대해 유지 보수가 용이하다.</li>
<li>숙련되면 여러 개를 설정해서 분산처리가 가능하다.</li>
<li>사용 언어와 환경이 바뀔 때, 다른 맵칭 정보를 연결해줄 수 있다.</li>
</ul>
<hr>
<h3 id="스프링-부팅-과정">스프링 부팅 과정</h3>
<p>이번에는 웹 프로젝트로서 스프링이 실행될 때, 부팅 순서를 알아보자.</p>
<h4 id="1-tomcat-실행">(1) Tomcat 실행</h4>
<p>웹 프로젝트니 당연히 서버가 먼저 실행되어야 한다. 따라서 Tomcat이 제일 먼저 실행된다. 그런데, 톰캣이 실행되면서 <code>src/main/webapp/web.xml</code>을 분석한다.</p>
<p>이때, web.xml파일을 보면 다음과 같은 코드가 있다.</p>
<pre><code class="language-xml">&lt;listener&gt;
&lt;listener-class&gt;org.springframework.web.context.ContextLoaderListener&lt;/listener-class&gt;
&lt;/listener&gt;</code></pre>
<p>이 코드는 new를 통해서 <code>ContextLoaderListener</code> 인스턴스를 생성한다.</p>
<h4 id="2-contextloaderlistener-생성">(2) ContextLoaderListener 생성</h4>
<p>해당 인스턴스 내부에는 다음과 유사한 코드가 숨겨져 있다.</p>
<pre><code class="language-java">AbstractApplicationContext ctx = new GenericXmlApplicationContext(&quot;context.xml&quot;);</code></pre>
<p>지난 시간에 배운 것처럼 위 코드는 스프링 컨테이너 인스턴스를 메모리에 생성하는 것이다. 즉, 톰캣이 켜지면서 <code>ContextLoaderListner</code>가 만들어지고, 이후 스프링 컨테이너가 call-back으로 만들어진다.</p>
<h4 id="3-spring-container-생성">(3) Spring Container 생성</h4>
<p>다만 스프링 컨테이너는 그 상단에 있는 경로를 참조한다.</p>
<pre><code class="language-xml">&lt;context-param&gt;
    &lt;param-name&gt;contextConfigLocation&lt;/param-name&gt;
    &lt;param-value&gt;/WEB-INF/spring/root-context.xml&lt;/param-value&gt;
&lt;/context-param&gt;</code></pre>
<p><code>root-context.xml</code>은 웹과 관련이 없는 jdbc, json에 대한 설정 문서이다. 결과적으로 서버가 동작하면서 스프링 컨테이너는 자신의 영역 안에 개발자가 설정해둔 bean을 보관하면서 만들어진다. 그리고 DI를 통해서 각각을 조립해둔다. </p>
<blockquote>
<p><strong>이렇게 부팅이 일차적으로 끝나고, 일시적인 대기 상태에 돌입한다.</strong></p>
</blockquote>
<hr>
<p>앞전 세팅이 끝나고 대기 상태인 서버에 Client가 요쳥(Request)하는 순간, 다음 단계가 실행된다.</p>
<h4 id="1-dispatcherservletds객체-생성">(1) DispatcherServlet(DS)객체 생성</h4>
<p>Tomcat이 요청을 받는 순간, 메모리에는 DispathcerServlet 객체가 적재된다.
앞 부분읜 <code>web.xml</code>의 하단을 보면 요청 시 다음의 프로세스가 실행된다고 적혀 있다.</p>
<pre><code class="language-xml">&lt;!-- Processes application requests --&gt;
&lt;servlet&gt;
    &lt;servlet-name&gt;appServlet&lt;/servlet-name&gt;
DispatcherServlet 객체 생성    &lt;servlet-class&gt;org.springframework.web.servlet.DispatcherServlet&lt;/servlet-class&gt;
    &lt;init-param&gt;
        &lt;param-name&gt;contextConfigLocation&lt;/param-name&gt;
생성 시, 참조하는 경로        &lt;param-value&gt;/WEB-INF/spring/appServlet/servlet-context.xml&lt;/param-value&gt;
    &lt;/init-param&gt;
    &lt;load-on-startup&gt;1&lt;/load-on-startup&gt;
&lt;/servlet&gt;</code></pre>
<h4 id="2-servlet-contextxml-참조">(2) servlet-context.xml 참조</h4>
<p>스프링 컨테이너 생성 시, <code>root-context.xml</code>를 참조한 것처럼 DS는 <code>servlet-context.xml</code>을 참조한다.</p>
<p>해당 문서는 웹 프로젝트과 관련된 설정이 담겨져 있는데 여기서 <strong>두 가지 동작</strong>이 일어난다.</p>
<p><strong><code>&lt;annotation-driven/&gt;</code></strong></p>
<p>위 문서를 참조하면 먼저 <code>@Controller</code> 처리된 컨트롤러들을 모두 분석하여 내부 URL이 어떤 것이 있는지 확인하고 그 정보를 담은 <code>HandlerMapping</code> 인스턴스를 만들어 둔다. 이 정보는 해당 컨트롤러에 어떤 url이 입력되어 있는지에 대한 것이다.</p>
<p>** ViewResolver 객체 생성**</p>
<p>그 다음으로는 <strong>ViewResolver</strong>라는 빈을 생성한다.</p>
<pre><code class="language-xml">&lt;beans:bean class=&quot;org.springframework.web.servlet.view.InternalResourceViewResolver&quot;&gt;
    &lt;beans:property name=&quot;prefix&quot; value=&quot;/WEB-INF/views/&quot; /&gt;
    &lt;beans:property name=&quot;suffix&quot; value=&quot;.jsp&quot; /&gt;
&lt;/beans:bean&gt;
</code></pre>
<p>이 부팅 순서의 요점은 servlet-context의 빈들은 요청이 없으면 만들어지지 않는다는 점이다. 즉, 생성 시점 자체가 첫 요청 이후이기 때문에 그전에는 사용할 수 없다.</p>
<hr>
<h2 id="3-스프링-기본-사용법">3. 스프링 기본 사용법</h2>
<h4 id="1-handlermapping">1) HandlerMapping</h4>
<p>스프링에서 <code>@RequestMapping(“/”)</code>의 역할은 MVC2에서 <code>if(uri.equals(“/”)){}</code> 와 같은 조건식과 똑같다. </p>
<h4 id="2-viewresolver">2) ViewResolver</h4>
<p>ViewResolver는 말 그대로 결과를 반영해서 보내줄 View를 찾는 기능이다. 사실 컨트롤러의 <code>return</code> 안에 직접 그 경로를 입력해줘도 된다.</p>
<p>하지만 그 경우 유지보수가 어려워지기 때문에 ViewResolver을 통해서 경로 String을 최대한 효율적으로 작성하고, 변경 시에도 ViewResolver만 수정하면 된다.</p>
<p>이러한 ViewResolver가 View를 찾고 DS에게 돌려주면, DS는 기본 forward 방식을 따라서 페이지 이동한다. (이때, JSP는 외부접근을 막아 보안을 하기 위해서 <code>WEB-INF</code> 폴더에 저장한다.</p>
<h4 id="3-redirect-하기">3) Redirect 하기</h4>
<p>일반 MVC2에서 응답을 하는 방법은 <code>forward</code>와 <code>redirect</code>를 response 객체를 통해서 개발자가 자유롭게 설정하였다. </p>
<p>하지만 스프링에서는 기본값은 <code>forward</code>이다. 때문에 생기는 문제 중 하나는 URL이 비워지지 않기 때문에 같은 요청을 반복해서 보낼 수 있다.</p>
<p>이를 해결하기 위해선 url을 비우는 리다이렉트를 해야하는데, 스프링의 경우 전제 조건이 있다.</p>
<ul>
<li>return 값을 <code>“redirect: view”</code>로 보낸다.</li>
<li>리다이렉트 하는 경로는 컨트롤러에 RequestMapping이 되어 있는 곳에만 가능하다.</li>
</ul>
<p>이렇게 <code>redirect</code>가 붙어 있으면 그 결과를 ViewResolver한테 DS가 보내지 않는다.</p>
<h4 id="4-프론트로부터-값-전달-받기">4) 프론트로부터 값 전달 받기</h4>
<p>스프링의 편의기능 중 하나가, 바로 넘겨 받는 값을 자동으로 DS가 채워준다는 것이다.</p>
<p>먼저 MVC2에서 값을 넘겨받기 위해서는 다음과 같이 프론트의 name값을 <code>getParameter();</code>로 끌어왔다.</p>
<pre><code class="language-java">String writer = request.getParameter(“writer”);
String contents = request.getParameter(“contents”);</code></pre>
<p>스프링에서도 이와 같이 받아올 수 있지만, 중요한 것은 컨트롤러 메서드에 기본 Servlet처럼 매개변수가 존재하지 않는다. 어떻게 해야될지 고민이 되겠지만 스프링에서는 개발자가 매개변수를 정하면 자동으로 그 값을 넣어준다.</p>
<p>매개변수는 3가지 경우로 나뉜다.</p>
<p><strong>(1) request, response</strong></p>
<p>이 경우는 MVC2와 동일하다. 다만 우리가 매개변수를 채워줄 뿐이다.</p>
<pre><code class="language-java">@RequestMapping(&quot;inputProc&quot;)
public String inputProc(HttpServlet request) { 
    String writer = request.getParameter(&quot;writer&quot;);
    String contents = request.getParameter(&quot;contents&quot;);

    System.out.println(writer + &quot; : &quot; + contents);

    return &quot;redirect:/&quot;; 
    }</code></pre>
<p><strong>(2) String name명</strong></p>
<p>동일하게 프론트에 표기된 name 속성의 값과 매개변수가 같다는 전제하에서는 단순 매개변수로도 쉽게 받아줄 수 있다.</p>
<pre><code class="language-java">@RequestMapping(&quot;inputProc&quot;)
public String inputProc(String writer, String contents) { 
    System.out.println(writer + &quot; : &quot; + contents);

    return &quot;redirect:/&quot;; 
    }</code></pre>
<p><strong>(3) DTO</strong></p>
<p>확장적으로 여러 개의 매개변수를 사용하지 않고, DTO 생성해두면 DS가 자동으로 값을 세팅해준다.</p>
<h4 id="-messagesdto-">[ MessagesDTO ]</h4>
<pre><code class="language-java">public class MessagesDTO {

    private int seq;
    private String writer;
    private String contents;
    private Date write_date;

    public MessagesDTO() {
        super();
    }

    public MessagesDTO(int seq, String writer, String contents, Date write_date) {
        super();
        this.seq = seq;
        this.writer = writer;
        this.contents = contents;
        this.write_date = write_date;
    }

    public int getSeq() {
        return seq;
    }

    public void setSeq(int seq) {
        this.seq = seq;
    }

    public String getWriter() {
        return writer;
    }

    public void setWriter(String writer) {
        this.writer = writer;
    }

    public String getContents() {
        return contents;
    }

    public void setContents(String contents) {
        this.contents = contents;
    }

    public Date getWrite_date() {
        return write_date;
    }

    public void setWrite_date(Date write_date) {
        this.write_date = write_date;
    }
}</code></pre>
<pre><code class="language-java">@RequestMapping(&quot;inputProc&quot;)
public String inputProc(MessagesDTO dto) { 
    System.out.println(dto.getWriter() + &quot; : &quot; + dto.getContents());

    return &quot;redirect:/&quot;; 
    }</code></pre>
<p>그 원리는 DTO를 작성하고, 그 안의 멤버필드의 이름과 name속성 값이 일치할 때, 이를 매개변수로 활용하면 DS가 Setter를 통해서 값을 세팅한다.</p>
<p>쉽게 말하면 다음 과정을 스프링이 실행해주는 것이다.</p>
<pre><code class="language-java">MessagesDTO dto = new MessagesDTO(); // 기본 생성자
dto.setWriter(writer);
dto.setContents(contents);</code></pre>
<p>다만 한 가지 주의점은 무조건 기본 생성자가 존재해야된다는 것이다. 우리가 흔히 사용할 때 기본 생성자를 덮어씌운 생성자만 활용하는데, 이 경우 스프링이 생성자를 만들어도 빈값이 존재하기 때문에 setter를 사용할 수 없어 400 bad request가 발생한다.</p>
]]></description>
        </item>
    </channel>
</rss>