1. 객체지향 디자인 패턴


객체지향 프로그램이 복잡해지면서 이를 간결하게 정리할 필요성이 생긴 관계로 '디자인 패턴'이라는 것이 생겼다. 

디자인 패턴 프로그래밍 형식을 정하는 일종의 약속이다.


객체 지향 프로그래밍 설계를 할 때 자주 발생하는 문제들을 피하기 위해 사용되는 패턴.


여러 사람이 협업해서 개발할 때 다른 사람이 작성한 코드, 기존에 존재하는 코드를 이해하는 것은 어렵다. 

이런 코드를 수정하거나 새로운 기능을 추가해야 하는데 의도치 않은 결과나 버그를 발생시키기 쉽고 

성능을 최적화시키기도 어렵다. 이로 인해 시간과 예산이 소모된다. 


디자인 패턴은 의사소통 수단의 일종으로서 이런 문제를 해결해준다. 

예를 들어 문제 해결의 제안에 있어서도 “기능마다 별도의 클래스를 만들고, 

그 기능들로 해야할 일을 한번에 처리해주는 클래스를 만들자.”라고 제안하는 것보다 

"Facade 패턴을 써보자."라고 제안하는 쪽이 이해하기 쉽다.


일반 프로그래머가 만나는 문제가 지구상에서 유일한 문제일 확률은 거의 없다. 

이미 수많은 사람들이 부딪힌 문제다. 따라서 전문가들이 기존에 해결책을 다 마련해 놓았다. 



2. 생성 패턴(추상 객체 인스턴스화)


2.1 팩토리(Factory Method)


상위 클래스와 하위 클래스가 있을 때, 팩토리 클래스를 사용하여 하위 클래스의 인스턴스를 생성하는 패턴.


Animal.java


1
2
3
4
5
6
public class Animal {
    Animal(){
        // 생성자
    }
    // 이하 Animal의 메소드들
}
cs


Dog.java


1
2
3
4
5
6
7
8
9
10
11
12
public class Dog extends Animal{
    Dog(){
        // 생성자
    }
    public Dog(String data){
        return;
    }
    // 이하 Dog의 메소드들
    public String toString(){
        return "Dog";
    }
}
cs


Cat.java


1
2
3
4
5
6
7
8
9
10
11
12
public class Cat extends Animal{
    Cat(){
        // 생성자
    }
     public Cat(String string) {
         return;
    }
    // 이하 Cat의 메소드들
    public String toString(){
        return "Cat";
    }
}
cs


Map.java


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import java.util.Iterator;
import java.util.LinkedList;
 
public class Map {
    
    public static void main(String[] args){
        
        // 저장된 파일로부터 동물을 가져오는 '맵 로드' 기능을 
        // lnkList에 초기 샘플데이터 넣었다고 가정
        LinkedList<String> lnkList = new LinkedList<String>();
        lnkList.add("Dog");
        lnkList.add("Cat");
 
        // Map은 말 그대로 맵의 구현 방법에 대해서만 서술되어야 하는데, 
        // 파일을 읽는 부분에서 '동물을 분류하는' 추가적인 책임이 포함되어있다. 
        // 만약 새 동물 Rat을 넣어야 한다면 전혀 상관없는 Map 클래스를 수정해야 할 것이다.
        
        // 1. 개선 전 방법 
        Iterator<String> iter = lnkList.iterator();        
        while (iter.hasNext()) {
            if(iter.equals("Dog")){
                Dog dog = new Dog();
                System.out.println(dog);
            }else if(iter.equals("Cat")){
                Cat cat = new Cat();
                System.out.println(cat);
            }
        }
 
        // 그래서 다양한 하위 클래스들을 생성하는(Factory : 공장) 클래스를 만들어 
        // 그 클래스에 책임을 위임하는 것이다.
        // 그러면 새 클래스 AnimalFactory를 만들어보자.
        
        // 2. 개선 후 방법 
        Iterator<String> factoryIter = lnkList.iterator();    
        while (factoryIter.hasNext()) {
           Animal animal = AnimalFactory.create(factoryIter.next());
           System.out.println(animal);
        }
    }
}
cs


AnimalFactory.java


1
2
3
4
5
6
7
8
9
10
11
12
13
public class AnimalFactory {
    static Animal create(String string) {
        if(string.equals("Dog")) {
            return new Dog(string);
        } else if(string.equals("Cat")) {
            return new Cat(string);
        } else if(string.equals("Rat")){
//            return new Rat(string);
        }
       //기타 동물의 생성자들
        return null;
    }
}
cs



팩토리를 사용하면 새 동물을 추가하는지의 여부에 상관없이 

다른 클래스를 수정할 필요가 없어져 단일 책임 원칙을 잘 지키는 코드가 된다.



2.2 빌더(Builder)


빌더 클래스는 인스턴스를 생성자를 통해 직접 생성하지 않고, 빌더라는 내부 클래스를 통해 간접적으로 생성하게 하는 패턴이다.


Jacket.java


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
class Jacket {
 
    int number;
    String name;
    double size;
 
    private Jacket(int number, String name, double size) {
        //Jacket 클래스 초기화
        this.number = number;
        this.name = name;
        this.size = size;
    }
 
    // 빌더 클래스는 인스턴스를 생성자를 통해 직접 생성하지 않고, 
    // 빌더라는 내부 클래스를 통해 간접적으로 생성하게 하는 패턴이다
    public static class Builder {
        int number=0;
        String name=null;
        double size=0d;
 
        public Builder() {
            //Builder 초기화
        }
 
        public Builder setNumber(int number) {
            this.number = number;
            return this;
        }
 
        public Builder setName(String name) {
            this.name = name;
            return this;
        }
 
        public Builder setSize(double size) {
            this.size = size;
            return this;
        }
 
        public Jacket build() {
            return new Jacket(number, name, size);
        }
    }
}
cs


JacketProduction.java


1
2
3
4
5
6
7
8
9
10
public class JacketProduction {
    public static void main(String args[]){
        createJacket();
    }
 
    public static void createJacket() {
        Jacket jacket = new Jacket.Builder().setNumber(1).setName("NorthFace").setSize(105).build();
        System.out.println(jacket.number+"  "+ jacket.name +"  "+ jacket.size);
    }
}
cs


1
1  NorthFace  105.0
cs


Builder 사용 이유


1. 클래스와 사용 대상의 결합도를 낮추기 위해


private 등의 접근 제한자로 제한하여 외부에서 임의로 접근하는 것을 막아 클래스와 사용대상의 결합도를 떨어뜨리고, 

대신 Builder라는 내부 클래스를 통해 해당 클래스를 간접적으로 생성한다.


2. 생성자에 전달하는 인수에 의미를 부여하기 위해


예를 들어서, 위에 제시된 예시에서 빌더 패턴이 없다고 가정하고 인스턴스를 생성하려면 

Jacket jacket = new Jacket(number, name, size); 이렇게 코드를 작성하여야 한다.


위의 예시에서는 인수가 세 개니까 그냥 저렇게 써도 큰 문제는 없지만, 

생성자에 전달하는 인수의 가짓수가 열 종류 가까이 되는 클래스의 경우에는 

고전적인 생성자 패턴으로는 인수를 전달하는 것이 상당히 비직관적이 된다.

(인수의 종류를 외워서 써넣어야되는 것뿐만 아니라, 인수의 순서까지 고려해야 한다!)

그래서 빌더 패턴을 통해 setXXX 형식으로 인수를 전달하면 한 눈에 보기에도 이것이 무슨 인수인지를 파악하기가 쉽다.




2.3 싱글톤(Singleton)



1
2
3
4
5
6
7
8
9
10
11
12
class Singleton {
 
    static final Singleton instance = new Singleton();
 
    private Singleton() {
        //초기화
    }
 
    public Singleton getInstance() {
        return instance;
    }
}
cs


키보드 리더, 프린터 스풀러, 점수기록표 등 클래스의 객체를 하나만 만들어야 하는 경우 사용한다. 

클래스 내에서 인스턴스가 단 하나뿐임을 보장하므로, 프로그램 전역에서 해당 클래스의 인스턴스를 바로 얻을 수 있고, 

불필요한 메모리 낭비를 최소화한다.


이 패턴에서는 생성자를 클래스 자체만 사용할 수 있도록 private 등의 접근제한자를 통하여 제한하여야 한다. 

생성자를 다른 곳에서도 사용할 수 있으면 그 곳에서도 인스턴스를 만들 수 있기 때문.


싱글톤 패턴을 사용하기 위해서는 반드시 접근제한자를 이용하여 외부의 접근을 막거나, 

final로 reference를 변경 불가능하게 설정하여야 한다. 

물론 생성자에 접근제한자를 사용하면 최소한 다른 인스턴스로 레퍼런스시키지는 못하겠지만, 

ClassName.singleton = null; 처럼 레퍼런스 자체를 지워버릴 수 있기 때문.

구현 방법에는 사전 초기화, 늦 초기화 등이 있다.


Eager initialization(사전 초기화)


클래스 로딩시에 인스턴스를 생성하는 방법이다. 위의 예시가 사전 초기화. 멀티스레드 환경에서의 

이중 객체 생성 문제가 없지만, 인스턴스를 호출하지 않아도 무조건 클래스를 초기화하기에 메모리 효율이나 연산 효율은 낮다.

Java에서는 static block initialization이라는 변종도 있다. 

클래스가 로딩될 때 최초 1회만 실행되는 static block을 통해 싱글톤 인스턴스를 초기화하는 방법인데, 구조적으로는 크게 다르지 않다.


Lazy initialization(늦 초기화)


인스턴스를 실제로 사용할 시점에서 인스턴스를 생성하는 방법이다. 세심한 방법을 쓰지 않으면 

위에 언급한 이중 객체 생성 문제가 발생할 가능성이 높으나, 

인스턴스를 실제로 사용하지 않는다면 메모리와 연산량을 아낄 수 있다는 장점이 있다.



3. 구조 패턴(객체 결합)


3.1 파사드(Facade)


파사드는 프랑스어 Façade에서 차용된 단어로 보통 건물의 출입구로 이용되는 정면 외벽 부분을 가리키는 말이다.

파사드 패턴은 시스템의 복잡성을 감추고, 사용자(Client)가 시스템에 접근할 수 있는 인터페이스(Interface)를 사용자(Client)에게 제공한다. 

따라서 파사드 패턴은 기존의 시스템에 인터페이스를 추가함으로써, 

복잡성을 감추기 위해 사용된다. 파사드 패턴은 구조적 패턴(Structural Pattern)에 포함된다.


1단계: 인터페이스를 생성한다.


1
2
3
public interface Coffee {
    void make();
}
cs



2단계: 그 인터페이스를 구현하기 위한 구체적인 클래스를 생성한다.


Americano.java


1
2
3
4
5
6
public class Americano implements Coffee {
   @Override
   public void make() {
      System.out.println("Americano::make()");
   }
}
cs


BanillaLatte.java


1
2
3
4
5
6
public class BanillaLatte implements Coffee {
   @Override
   public void make() {
      System.out.println("BanillaLatte::make()");
   }
}
cs


Cappuccino.java


1
2
3
4
5
6
public class Cappuccino implements Coffee {
   @Override
   public void make() {
      System.out.println("Cappuccino::make()");
   }
}
cs



3단계: 파사드 클래스를 생성한다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class CoffeeMaker {
    private Coffee americano;
    private Coffee banillaLatte;
    private Coffee cappuccino;
 
    public CoffeeMaker() {
        americano = new Americano();
        banillaLatte = new BanillaLatte();
        cappuccino = new Cappuccino();
    }
 
    public void makeAmericano(){
        americano.make();
    }
    
    public void makeBanillaLatte(){
        banillaLatte.make();
    }
 
    public void makeCappuccino(){
        cappuccino.make();
    }
}
cs



4단계: 다양한 종류의 형태를 만들기 위해 파사드를 사용한다.


1
2
3
4
5
6
7
8
9
public class FacadePatternDemo {
    public static void main(String[] args) {
        CoffeeMaker coffeeMaker = new CoffeeMaker();
 
        coffeeMaker.makeAmericano();
        coffeeMaker.makeBanillaLatte();
        coffeeMaker.makeCappuccino();
    }
}
cs



5단계: 결과값을 확인한다.


1
2
3
Americano::make()
BanillaLatte::make()
Cappuccino::make()
cs



4. 행위 패턴(객체 간 커뮤니케이션)


4.1 반복자(iterator)


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
 
public class IteratorTest {
    public static void main(String args[]){
        
        // List for each
        List<Integer> list = new ArrayList<Integer>();
        list.add(1); list.add(2); list.add(3);
        
        for(Integer a : list){
            System.out.println(a);
        }
        
        // Iterator
        LinkedList<String> lnkList = new LinkedList<String>();
        lnkList.add("Dog"); lnkList.add("Cat");
        
        Iterator<String> iter = lnkList.iterator();
        
        while(iter.hasNext() == true) {
          Object object = iter.next();
          System.out.println(object);
        }
    }
}
cs


1
2
3
4
5
1
2
3
Dog
Cat
cs



5. 기타


5.1 MVC


최근 애플리케이션 개발에 있어 상당히 중요한 모델이 된 패턴으로 Model, View, Controller 세 가지 부분으로 이루어져 있다. 

Model은 자료(Data)를 생성, 저장, 처리하는 역할을 하는 부분이다. 

View는 Model로부터 받은 자료를 여러 가지 형태로 사용자에게 보여주는 역할을 한다. 

Controller란 소프트웨어의 흐름을 제어하는 것으로 View와 Model 사이에서 관계를 설정하여 주는 부분을 말한다. 

Controller는 Model이나 View가 바뀌더라도 수정 없이 작동되어야 한다.



테스트 작성한 소스 첨부


myTest5.zip




 

1. 객체 지향 프로그래밍이란?

 

객체 지향 프로그래밍(Object-Oriented Programming), 줄여서 OOP.

프로그램을 어떻게 설계해야 하는지에 대한 일종의 개념이자 방법론.

 

프로그램을 단순히 데이터와 처리 방법으로 나누는 것이 아니라, 

프로그램을 수많은 '객체'라는 기본 단위로 나누고 이 객체들의 상호작용으로 서술하는 방식이다. 

 

객체를 데이터의 묶음으로만 착각하기 쉬운데, 그보다는 하나의 '역할'을 수행하는 메소드와 데이터의 묶음으로 봐야 한다.

 

1.1 절차적 프로그래밍 방식

 

입력을 받아 명시된 순서대로 처리한 다음, 그 결과를 내는 것

어떻게 어떤 논리를 어떤 순서대로 써나가는 것인가로 간주되었다. 

즉, 프로그램 자체가 가지는 기능에 대해서만 신경을 썼지, 이 프로그램이 대체 어떤 데이터를 취급하는 것인가에는 

그다지 관심이 없었던 것이다.

 

1.2 구조적 프로그래밍 방식

 

절차적 프로그래밍 방식을 개선하기 위해 나온 방식으로

프로그램을 함수(procedure) 단위로 나누고 

프로시져끼리 호출을 하는 것이 구조적 프로그래밍 방식이다. 

프로그램이라는 큰 문제를 해결하기 위해 그것을 몇개의 작은 문제들로 나누어 해결하기 때문에 

하향식(Top-down) 방식이라고도 한다.

 

1.3 객체 지향 프로그래밍 방식

 

구조적 프로그래밍 박식을 개선하기 위해 나온 방식으로

객체 지향 프로그래밍이다. 

큰 문제를 작게 쪼개는 것이 아니라, 먼저 작은 문제들을 해결할 수 있는 객체들을 만든 뒤, 

이 객체들을 조합해서 큰 문제를 해결하는 상향식(Bottom-up) 해결법을 도입한 것이다. 

이 객체란 것을 일단 한번 독립성/신뢰성이 높게 만들어 놓기만 하면 그 이후엔 

그 객체를 수정 없이 재사용할 수 있으므로 개발 기간과 비용이 대폭 줄어들게 된다.

 

OOP를 사용하면 코드의 중복을 어느 정도 줄일 수 있고 입력 코드, 

계산 코드와 결과 출력 코드 등 코드의 역할 분담을 좀 더 확실하게 할 수 있어서 가독성이 높아질 수 있다.

 

 

2. 

객체 지향 프로그래밍 요소

 

2.1 캡슐화

 

캡슐화는 프로그램의 세부 구현을 외부로 드러나지 않도록 특정 모듈 내부로 감추는 것이다. 

캡슐화는 객체지향 언어를 구성하는 주요 요소이지만 객체지향 언어에서만 사용되는 개념은 아니다. 

내부의 구현은 감추고(=정보 은닉) 모듈 내에서의 응집도를 높이며, 외부로의 노출을 최소화하여 

모듈 간의 결합도를 떨어뜨리는 개념은 거의 대부분의 언어에 녹아있다.

 

많은 객체지향 언어에서 사용되는 클래스를 기준으로 보면, 

클래스 외부에서는 바깥으로 노출된 특정 메소드에만 접근이 가능하며 클래스 내부에서 

어떤 식으로 처리가 이루어지는지는 알지 못하도록 설계된다.

 

일반적으로 세 종류의 접근 제한이 사용된다.

 

public : 클래스의 외부에서 사용 가능하도록 노출시키는 것이다.

 

protected : 다른 클래스에게는 노출되지 않지만, 상속받은 자식 클래스에게는 노출되는 것이다.

 

private : 클래스의 내부에서만 사용되며 외부로 노출되지 않는다.

 

 

2.2 상속

 

상속은 자식 클래스가 부모 클래스의 특성과 기능을 그대로 물려받는 것을 말한다. 

기능의 일부분을 변경해야 할 경우 자식 클래스에서 상속받은 그 기능만을 수정해서 다시 정의하게 되는데, 

이러한 작업을 '오버라이딩(Overriding: 재정의)'이라고 한다. 

상속은 캡슐화를 유지하면서도 클래스의 재사용이 용이하도록 해 준다.

 

 

2.3 다형성

 

하나의 함수명 등이 상황에 따라 다른 의미로 해석될 수 있는 것을 말한다. 

이를 '오버로딩(Overloading: 중복 정의/다중 정의)'이라고 한다.

 

함수 오버로딩

C++과 C#, Java에서는 함수 오버로딩을 통해 동일한 이름의 함수를 매개변수에 따라 다른 기능으로 동작하도록 

할 수 있다.  함수 오버로딩을 너무 자주 사용하면 해당 함수가 어디서 오버로딩되었는지 찾기 어려워질 수 있으므로, 

지나친 남발은 자제하는 것이 좋다.

 

 

2.4 장점

 

이 OOP 특성 덕분에 개발시간 단축(잘 만들어진 클래스는 재사용성을 보장한다) 

정확한 코딩(구현 목적을 위해 클래스를 나눌 수 있으니 구현 단위와 목표가 뚜렷해진다.)이 가능하다.

 

 

3. 객체지향의 원칙[ 5원칙(SOLID) ]

 

객체지향에서 꼭 지켜야 할 5개의 원칙을 말한다. 

일단 한번 보면 개념은 알아 듣긴 하지만 막상 실현하려면 생각보다 어려움이 따른다. 

이 5개의 원칙의 앞글자를 따서 SOLID라고도 부른다.

 

 

3.1 SRP(Single Responsibility Principle) : 단일 책임 원칙 

 

객체는 오직 하나의 책임을 가져야 한다. (객체는 오직 하나의 변경의 이유만을 가져야 한다.)

사칙연산 함수를 가지고 있는 계산 클래스가 있다고 치자. 이 상태의 계산 클래스는 오직 사칙연산 기능만을 책임진다. 

만일 프로그램이 대대적으로 공사를 들어가게 되더라도 계산 클래스가 수정될만한 사유는 누가 봐도 사칙연산 함수와 

관련 된 문제 뿐이다.  이처럼 단일 책임 원칙은 클래스의 목적을 명확히 함으로써 구조가 난잡해지거나 수정 사항이 

불필요하게 넓게 퍼지는 것을 예방하고 기능을 명확히 분리할 수 있게 한다.

위의 원칙이 제대로 지켜지지 않으면 어떻게 될까? 

어떤 프로그래머가 위의 계산 클래스를 통해 GUI를 가지는 계산기 프로그램을 개발하고 있다. 

그런데 중간에 귀찮다고 GUI 관련 코드를 계산 클래스에 넣어버렸다. 

이렇게 되면 계산 클래스는 계산과 GUI라는 두 가지 책임을 지게 되는데 만일 GUI 관련 수정 사항이 발생하게 되면 

별 상관도 없어보이는 계산 클래스를 고치게 된다. 

이처럼 하나의 클래스가 두 가지 이상의 책임을 지니게 되면 클래스의 목적이 모호해지고 기능을 수정할 때 

영향을 받는 범위도 커져서 유지보수가 힘들어지며[1] 결국 작성한 본인조차도 이게 정확히 뭐하는 클래스인지 

명확히 설명할 수가 없는 스파게티 코드가 되어버린다.

 

 

3.2 OCP(Open-Closed Principle) : 개방-폐쇄 원칙

 

객체는 확장에 대해서는 개방적이고 수정에 대해서는 폐쇄적이어야 한다는 원칙이다. 

즉, 객체 기능의 확장을 허용하고 스스로의 변경은 피해야 한다.

 

예를 들면 도롱뇽과 개구리가 있다. 당신은 이런저런 공통사항을 생각하며 메소드와 필드를 정의한다. 

둘다 이동속도는 다르지만 기본 이동은 도룡뇽은 기어다니고 개구리는 기어다니며 점프하고를 반복한다.

하지만 개구리 같은 동물의 움직임을 구현할 때 애로사항이 있을 것 같다.

 

이동 메소드에서 이동 패턴을 나타내는 코드를 별도의 메소드로 분리하고, 구현을 하위 클래스에 맡긴다. 

그러면 개구리 클래스에서는 이동 패턴 메소드만 재정의하면 동물 클래스의 변경 없이 

기어다니며 점프하는 움직임을 보여줄 수 있다! '동물' 클래스의 '이동' 메소드는 수정할 필요조차 없다(수정에 대해선 폐쇄). 

그냥 개구리 클래스의 이동 패턴 메소드만 재정의하면 그만인 것이다(확장에 대해선 개방).

 

 

3.3 LSP(Liskov Substitution Principle) : 리스코프 치환 원칙

 

자식 클래스는 언제나 자신의 부모 클래스를 대체할 수 있다는 원칙이다. 

즉 부모 클래스가 들어갈 자리에 자식 클래스를 넣어도 계획대로 잘 작동해야 한다는 것. 

상속의 본질인데, 이를 지키지 않으면 부모 클래스 본래의 의미가 변해서 다형성을 지킬 수 없게 된다.

 

또다시 예를 들면, 컴퓨터용 '마우스' 클래스가 있다고 치자. 

컴퓨터에 있는 ps/2 포트나 usb 포트를 통해 연결할 수 있고, 마우스를 바닥에 대고 움직이면 

컴퓨터가 신호를 받아들인다는 것을 안다.  사용 면에서는 왼쪽과 오른쪽 버튼, 그리고 휠이 있어 사용자가 

누르거나 굴릴수 있을 것이다.  마우스가 볼마우스든 광마우스든, 아니면 GPS를 이용하건 간에 아무튼 

사용자는 바닥에 착 붙여 움직일 것이고, 모든 마우스는 예상대로 신호를 보내 줄 것이다. 

 

또한 만약 추가적인 특별한 버튼이 있는 마우스(상속)라도 그 버튼의 사용을 제외한 다른 부분은 

보통의 마우스와 다를 바 없으므로 사용자는 그 마우스의 그 버튼이 뭔 역할을 하던간에 문제 없이 잘 사용한다. 

여기까지 나온 마우스들은 LSP를 잘 지킨다고 볼 수 있다.

 

하지만 오른쪽/왼쪽 버튼 대신 옆쪽 버튼을 사용하는 펜마우스를 처음으로 접하게 되면 사용자는 

평소 보던 버튼을 누를 수 없다며 이상을 호소할 것이다. 이런 경우 LSP를 전혀 지키지 못 하는 것이다.

 

 

3.4 ISP(Interface Segregation Principle) : 인터페이스 분리 원칙

 

클라이언트에서 사용하지 않는 메서드는 사용해선 안된다. 그러므로 인터페이스를 다시 작게 나누어 만든다. 

OCP와 비슷한 느낌도 들지만 엄연히 다른 원칙이다. 하지만 ISP를 잘 지키면 OCP도 잘 지키게 될 확률이 

비약적으로 증가한다.

 

게임을 만드는데 충돌 처리와 이팩트 처리를 하는 서버를 각각 두고 

이 처리 결과를 (당연히) 모두 클라이언트에게 보내야 한다고 가정하자. 

그러면 아마 Client라는 인터페이스를 정의하고 그 안에 충돌전달()과 이펙트전달(이펙트)를 넣어놓을 것이다. 

그리고 충돌 서버와 이펙트 서버에서 이 인터페이스를 구현하는 객체들을 모아두고 있으며, 때에 따라 적절히 신호를 보낸다. 

하지만 이렇게 해두면 충돌 서버에겐 쓸모없는 이펙트전달 인터페이스가 제공되며, 

이펙트 서버에겐 쓸모없는 충돌전달 인터페이스가 제공된다.  이를 막기 위해선 Client 인터페이스를 쪼개 

이펙트전달가능 인터페이스와 충돌전달가능 인터페이스로 나눈 뒤, 충돌에는 충돌만, 

이펙트에는 이펙트만 전달하면 될 것이다. 

또한 Client 인터페이스는 남겨두되 이펙트전달가능과 충돌전달가능 이 둘을 상속하면 된다.

 

 

3.5 DIP(Dependency Inversion Principle) : 의존성 역전 원칙

 

추상성이 높고 안정적인 고수준의 클래스는 구체적이고 불안정한 저수준의 클래스에 의존해서는 안된다는 원칙으로서, 

일반적으로 객체지향의 인터페이스를 통해서 이 원칙을 준수할 수 있게 된다. 

(상대적으로 고수준인) 클라이언트는 저수준의 클래스에서 추상화한 인터페이스만을 바라보기 때문에, 

이 인터페이스를 구현한 클래스는 클라이언트에 어떤 변경도 없이 얼마든지 나중에 교체될 수 있다..

 
 

 


1. PL/SQL이란?


- PL/SQL 은 Oracle’s Procedural Language extension to SQL 의 약자 이다.

- SQL문장에서 변수정의, 조건처리(IF), 반복처리(LOOP, WHILE, FOR)등을 지원하며,

오라클 자체에 내장되어 있는 Procedure Language 이다.

- DECLARE문을 이용하여 정의되며, 선언문의 사용은 선택 사항 이다.

- PL/SQL 문은 블록 구조로 되어 있고 PL/SQL자신이 컴파일 엔진을 가지고 있다.


2. PL/SQL의 장점


- PL/SQL 문은 BLOCK 구조로 다수의 SQL 문을 한번에 ORACLE DB로 보내서 처리하므로 수행속도를 향상 시킬수 있다.

- PL/SQL 의 모든 요소는 하나 또는 두개이상의 블록으로 구성하여 모듈화가 가능하다.

- 보다 강력한 프로그램을 작성하기 위해서 큰 블록안에 소블럭을 위치시킬 수 있다.

- VARIABLE, CONSTANT, CURSOR, EXCEPTION을 정의하고, SQL문장과 Procedural 문장에서 사용 한다.

- 단순, 복잡한 데이터 형태의 변수를 선언 한다.

- 테이블의 데이터 구조와 컬럼명에 준하여 동적으로 변수를 선언 할 수 있다.

- EXCEPTION 처리 루틴을 이용하여 Oracle Server Error를 처리 한다.

- 사용자 정의 에러를 선언하고 EXCEPTION 처리 루틴으로 처리 가능 하다.



3. 오라클 프로시저 샘플로 만들기


다음은 새직원의 대한 연차를 +1씩 업데이트치는 루프를 프로시저로 만든 예제이다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
CREATE OR REPLACE PROCEDURE INTRANET.JOB_AUTO_VAC_NEW_EMPLOYEE
IS
    --FOR문을 위한 커서 생성
    CURSOR EMP_VAC_CUR IS
     SELECT * FROM(
        SELECT EMP_NO
                 , EMP_NM
                 , VAC_TOTAL_CNT
                 , JOIN_DT
                 , TO_CHAR(TRUNC(A.JOIN_DT, 'DD'),'YYYY') AS JOIN_YEAR
          FROM EMP_EMPLOYEE A
       ) A1
      WHERE A1.JOIN_YEAR = TO_CHAR(TRUNC(SYSDATE, 'DD'),'YYYY');
      
    total_cnt VARCHAR2(10);
BEGIN
    FOR EMP_VAC IN EMP_VAC_CUR LOOP
        
        -- 자신의 연차+1 을 변수에 저장
        total_cnt := EMP_VAC.VAC_TOTAL_CNT + 1;
        
        UPDATE EMP_EMPLOYEE A
              SET VAC_TOTAL_CNT =  total_cnt
         WHERE EMP_NO = EMP_VAC.EMP_NO;
 
    END LOOP;
END;
/
 
cs


1. CREATE OR REPLACE PROCEDURE 사용해서 프로시저를 만들거나 교체한다.

2. CURSOR를 만들고 만든 커서를 VAC_CUR을 가지고 BEGIN 과 END사이 FOR반복을 돌린다.

3. FOR반복을 돌며 EMP(직원)의 현재연차를 가져와 현재연차+1을 하여 변수에 저장한다.

4. UPDATE를 하나씩 쳐주면서 반복한다.

5. 반복이 끝나면 종료한다.



4. 프로시저 실행하기


1
EXECUTE INTRANET.JOB_AUTO_VAC_NEW_EMPLOYEE
cs



5. 오라클 잡 등록하기


오라클 잡(ORACLE JOB)을 등록하여 원하는날짜와 시간대에 프로시저가 실행될수 있게 스케쥴링 할 수 있다.


toad를 사용한다면 create job을 통해 실행될 날짜를 설정할수 있고 어떤 프로시저를 실행할지 명시할 수 있다.



오라클 잡을 생성하게 되면 다음과 같이 PL/SQL 문이 만들어지고 job에 대한 상세한 내용을 다시 확인할 수 있다. 




내용을 풀이하면 job의 간격은 매월 1일에 실행할 것이고 다음실행 날짜는 2019년 2월 1일에

INTRANET 프로시저를 실행하겠다는 것이다. 

이 job은 2019년 2월 1일에 실행되고 다음 실행날짜는 3월 1일로 설정된다.






1. EC2에서 우분투 인스턴스 하나를 생성한다.


2. putty로 원격 접속해 다음 cmd line을 이용해 실습환경을 셋팅한다.



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
1. 최신상태 업데이트
sudo apt-get update;
 
2. apache 설치
sudo apt-get install apache2
 
3. php 설치
sudo apt-get install php
 
4. MySQL 데이터베이스 서버 설치
sudo apt-get install mysql-server
 
5. MySQL 데이터베이스 클라이언트 설치
sudo apt-get install mysql-client
 
6. php와 mysql 연동
sudo apt-get install php-mysql;
 
7. 아파치 php 연동
sudo apt-get install libapache2-mod-php
 
8. 아파치 재시작
sudo service apache2 restart;
cs


3. 명령어를 통해 다 설치가 되면 웹브라우저를 열어 퍼블릭 DNS(IPv4) 를 입력한다.


 3.1 웹서버 아파치가 제대로 설치되었다면 ubuntu 기본페이지가 잘나온다.




 3.2 index.php 파일을 하나 만들어 phpinfo(); 내장함수를 호출해 php정보가 잘 설치되었는지 확인한다.





4. AWS RDS를 생성한다.






mariadb를 선택하고 비용문제가 있으니 프리티어를 꼭 체크해준다.

프리티어를 체크하면 다음단계로 기본설정으로 셋팅되어 진행된다. 






db 인스턴스 이름을 정하고 db에 접근하기 위한 username과 password를 지정한다.(꼭 기억해야함)


기본적으로 자신이 사용하고 싶은 데이터베이스 스키마를 생성할수 있다.

데이터베이스 이름은 o2로 지정하고 생성한다.




RDS 설치가 완료되면 

원활한 테스트환경을 위해 보안그룹을 열어준다.

자신의 RDS의 보안그룹으로 들어가 Inbound를 위치무관으로 설정한다.





테스트 이후에 보안그룹을 재설정하면 된다.



5. mysql이 잘 설치 되었는지 확인하기 위해 접속해본다.




1
2
mysql -h 퍼블릭 DNS(IPv4) 주소 -u RDS계정 -p
mysql -h mydb1.ceczqdx83uhe.ap-northeast-2.rds.amazonaws.com -u sanghyun -p
cs




잘 접속되었다면 rds를 생성할때 기본으로 만들었던 o2 데이터베이스가 잘 만들어졌는지 확인하고

간단한 쿼리도 날려본다.




1
2
3
4
mysql에 접속이 되었다면 
show databases;
use o2;
select * from topic;
cs




6. GUI 환경에서 db를 보고싶으면 heidisql을 깔아 

호스트명에  RDS 엔드포인트를 넣고 마스터 username과 password를 넣고 자신이 생성한 데이터베이스까지 넣어 접속해본다.





7  데이터베이스 스키마의 테이블을 생성하고 샘플데이터를 몇개 넣어서 확인한다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/* 테이블 생성 */
CREATE TABLE `topic` (
`id` int(11NOT NULL AUTO_INCREMENT,
  `title` varchar(100NOT NULL,
  `description` text NOT NULL,
  `author` varchar(30NOT NULL,
  `created` datetime NOT NULL,
  PRIMARY KEY (id)
ENGINE=InnoDB DEFAULT CHARSET=utf8;
 
/* 테이블 조회 */
select * from topic;
 
/* 샘플 데이터 삽입 */
INSERT INTO `topic` (`id``title``description``author``created`VALUES(1'harry potter','magic is power''sanghyun', now());
INSERT INTO `topic` (`id``title``description``author``created`VALUES(2'lord of the ring''ring is mine''sanghyun', now());
INSERT INTO `topic` (`id``title``description``author``created`VALUES(3'venom''black venom is fighter''sanghyun', now());
INSERT INTO `topic` (`id``title``description``author``created`VALUES(4'spiderman','spider is red''sanghyun', now());
cs






8. php파일 만들어 db 연결하기



1
2
3
4
5
6
7
8
9
10
11
// 경로 이동해
cd /var/www/html
 
// db.php 만든다.
sudo vi db.php
 
// rds.php 만든다.
sudo vi rds.php
 
// rds_receiver.php 만든다.
sudo vi rds_receiver.php
cs


db.php 내용은 다음과 같다.


1
2
3
4
5
<?php
// mysqli_connect("엔드포인트주소","사용자이름","비밀번호","db스키마명");
$write = mysqli_connect("mydb2.ceczqdx83uhe.ap-northeast-2.rds.amazonaws.com","sanghyun","abcdefg1234","o2");
$read = mysqli_connect("mydb2.ceczqdx83uhe.ap-northeast-2.rds.amazonaws.com","sanghyun","abcdefg1234","o2");
?>
cs



rds.php 내용은 다음과 같다.



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<!DOCTPYE>
<html>
 <head><meta charset="utf-8"></head>
  <body>
  <form action="rds_receiver.php" method="post">
       <p>
         제 목 :
         <input type="text" name="title">
       </p>
       <p>
         본 문 :
         <textarea name="description">
         </textarea>
       </p>
       <p><input type="submit"></p>
   </form>
   <ul>
<?php
include('db.php');
$sql="SELECT * FROM topic";
$result=mysqli_query($read,$sql);
 
while($row=mysqli_fetch_assoc($result)){
 $title=mysqli_real_escape_string($read,$row['title']);
 print("<li>{$title}</li>");
}
?>
    </ul>
   </body>
</html>
cs



rds_receiver.php 내용은 다음과 같다.



1
2
3
4
5
6
7
8
<?php
include('db.php');
$title = mysqli_real_escape_string($write$_POST['title']);
$description = mysqli_real_escape_string($write$_POST['description']);
$sql = "INSERT INTO topic(title, description, author, created) VALUES('{$title}','{$description}','sanghyun',NOW())";
mysqli_query($write,$sql);
header("Location: rds.php");
?>
cs



결과를 확인하면

select와 insert 모두 잘 되는 것을 확인할 수 있었다.






--------------------------------------------------------------------------------------------------------------------------



9. AWS server characterset 변경하기


데이터에 한글을 넣으면 ?나 특수문자 등 깨지는 현상 발생한다면 charset을 utf8로 바꿔줘야한다.





Server characterset과 Db characterset을 utf로 바꾸려면 aws rds의 파라미터그룹으로 접근해

파라미터 편집을 통해 캐릭터셋을 변경해줘야한다. 파라미터 그룹에서 






utf8로 바꾼뒤 db파라미터구성을 수정하여 DB 인스턴스를 재부팅하면 잘 적용되어 나온다.







출처 : https://opentutorials.org/course/2717/11268







1. 구글에 aws marketplace라고 검색하여 공식사이트에 들어가서 wordpress를 검색한다.






2. 환경설정으로 진입해






3. 버전을 확인하고 지역을 서울로 맞추고 launch한다.





4. launch 방법으로는 EC2를 선택하여 진행한다.





5. AWS AMI 선택이 완료된 이후이므로 

인스턴스 타입, 구성, 스토리지, 태그, 보안 그룹을 설정하여 시작한다.

(https://shlee0882.tistory.com/180?category=777755 참고)





6. WordPress 웹서버가 구축되면 퍼블릭 DNS와 IP로 접근이 가능하다.




7. 이제 워드프레스 관리자가 되어야한다.


사용설명서에 /wp-admin 을 붙여서 관리자로 진입하라고 안내하고 있다.

기본 서버 관리자는 'user'이고 비밀번호는 어떻게 얻는지 링크를 통해 안내하고 있다.




웹브라우저에서 ip주소/wp-admin 을 붙여서 url 이동하면 관리자 화면이 나오는데 username은 'user'

password를 알아야한다.





EC2 인스턴스 설정에서 > 시스템 로그 가져오기 




로그를 통해 암호를 확인한다.





아이디와 비밀번호를 넣고 접속하면 성공






이후 관리자의 여러기능을 활용해 수정하고 포스팅하여 자신의 블로그를 운영할 수 있다.








출처 : https://opentutorials.org/course/2717/11268





1. AWS 프리티어로 가입 한다. 






가입은 매우 간단하며 자신의 카드를 등록해야하고 인증과정에서 1달러가 결제된다.

카드 등록시 visa, mastercard인지 확인해야 한다. 본인은 체크visa카드로 가입을 진행하였다.



2. AWS > EC2 > 인스턴스 시작 


가입을 완료하고 AWS 많은 서비스 중 EC2 서비스를 선택하여 인스턴스 시작부분으로 진입한다.






3. 우분투 서버 만들기




1단계 우분투 이미지 선택 후





2단계 인스턴스 유형을 선택한다.







3단계 인스턴스 세부 정보 구성은 기본셋팅구성을 가져간다.






4단계 스토리지도 프리티어에서 사용할수 있는 기본 셋팅 되어있는 설정 그대로 가져간다.  


다음을 눌러 태그추가 단계에 들어오면






5단계 태그 추가를 통해 서버의 키(Name)와 값(Value)를 넣어준다.

 





6단계 보안 그룹 구성에 진입하면 SSH는 기본으로 잡혀있고 내 IP만 접속가능하게 설정하고

HTTP 추가를 하고 위치무관하게 잡아준다.






7단계는 지금까지 셋팅한 인스턴스를 어떻게 만들지 점검하고 생성을 누른다.






인스턴스 시작을 하면 키페어 생성 팝업이 나타난다.

키페어 이름은 원하는 것으로 정하고, 키페어 다운로드를 통해 암호화된 비밀번호를 다운받는다.

이미 만들어 놓은 키페어가 있다면 기존 키페어를 사용해도 된다.






다음과 같이 running 상태가 나타나면 서버가 잘 구동된 것이다.






생성한 ubuntu_server를 마우스 우클릭으로 연결하면



r



인스턴스에 연결하는 정보가 뜨게 된다.

퍼블릭 DNS를 가지고 putty나 xshell을 이용해 원격접속하여 제어할 수 있다.




3.1 xshell을 이용해 접속하는 방법



xshell을 실행해 세션의 퍼블릭 DNS의 내용을 호스트 값에 채워넣고





사용자 인증 탭의 사용자 키 부분을 연결시켜준다.






정보를 저장후 연결을 하면 원격으로 ubuntu_webserver가 잘 연결된 것을 확인 할 수 있다.






3.2 putty를 이용해 접속하는 방법



putty를 깔게되면 PUTTYgen이라는 프로그램도 같이 깔리게 된다.


자신의 인스턴스에서 지정한 키페어 이름을 확인하고 

키페어 암호를 파일형식으로 다운받아 


PUTTYgen을 실행시키고 Load를 눌러 다운받은 키페어 파일을 지정하고

Save private key를 눌러 재저장한다. 





ppk파일 형식으로 바뀐 파일을 PuTTY를 실행시켜 SSH > Auth > Browse 에 지정해주고




퍼블릭 DNS를 Host Name에 넣어준다.






연결을 하게 되면 ubuntu 로 로그인하여 접속한다.









-------------------------------------------------------------------------------------------------------------------------------------



3.3 우분투 서버 접속 방법



putty나 xshell을 이용해 접속이 되었다면 cmd명령어를 사용한다.


sudo apt-get update; 

cmd명령어를 통해 최신 업데이트 목록을 갱신시키고




sudo apt-get install apach2;

명령어를 넣어 웹서버 구동에 필요한 아파치2를 설치 한다.

설치 후 서버를 자동으로 실행시켜주는데 

웹브라우저에서 퍼블릭 DNS나 IP주소를 넣어 접속하면 apach2 기본 페이지가 잘 뜨는것을 확인할 수 있다. 



cd /var/www/html

명령어를 이용해 경로로 이동한뒤 sudo rm index.html 로 파일을 지우고 sudo nano index.html 로 파일을 재생성하여 코딩한다.





잘 반영되었는지 확인한다.









--------------------------------------------------------------------------------------------------------------------------------------------





4. 윈도우 서버 만들기




1단계에서 윈도우 서버 이미지를 선택하고






2,3 단계는 기본 설정으로 가져가고

4단계에서 윈도우 서버 기본크기가 30GB 이상인지 확인한다.






5단계에서 서버의 이름을 설정하고






6단계에서 보안그룹 설정을 통해 http가 위치 무관하게 어디든 접속 가능하게 설정한다.






7단계를 통해 인스턴스가 생성된 후 마우스 오른쪽 버튼 연결을 누른다.






생성한 키페어를 불러와 인스턴스에 연결시켜준다.










암호해독을 눌러 암호를 디코드한다.






원격 데스크톱 파일다운로드를 눌러 실행하면 






윈도우 서버로 연결된다.


윈도우 서버에서 Server Manager를 찾아서 실행 시킨뒤 Local Server에서 IE 보안 관련설정을 OFF해준다.






Service Manager에서 Manage > Add Roles and Features 를 눌러






Web Server(IIS) 를 설치해준다.






설치가 완료되면 Service Manager에 IIS가 생기게 되고 IIS Manager를 실행한다.





explore를 눌러







해당 위치에 메모장을 사용하여 index.html 파일을 간단하게 만들어 저장한다.







윈도우 서버컴퓨터에서 http://localhost/index.html 접속하면 잘나오는것을 확인했다.





로컬위치가 아닌 퍼블릭 DNS에서도 잘나오는지 확인이 필요해 AWS 콘솔에서 DNS주소에 index.html 붙여서 접속한다.







잘 반영되어 나오는 것을 확인할 수 있다.







출처 : https://opentutorials.org/course/2717/11268







1. EC2 인스턴스란?


EC2 인스턴스는 aws 컴퓨터를 임대하여 할당받는 것을 말한다.

물리적 컴퓨터 기계 위에 가상머신을 올려 

현재는 구성할 수 없는 저렴한 컴퓨터부터 강력한 비싼 컴퓨터까지 탄력적으로 만들 수 있다.  



2. aws ami


aws에서는 인스턴스를 생성할때 웹서버로 구성할 운영체제를 선택할 수 있다.

linux, ubuntu, window server 등 자신이 원하는 운영체제를 가상머신위에 올릴수 있다.



웹서버를 선택하여 처음부터

자신이 만들고 싶은 사이트를 설치 및 구성 하는 것은 시간도 많이 소요되고 어려운 일이다.


그래서 aws에서는 구글마켓플레이스처럼 

사용자가 원하는 사이트를 바로 구축하여 운영할 수 있게 

기본 셋팅을 이미지 형태로 제공하고 있다.

자신이 블로그를 만들고 싶다면 대표적으로 워드프레스를 검색하여 인스턴스로 바로 만들수 있다.

 


3. scale up 전략




예전 전통적인 모델 같은 경우 웹 서버를 구축하여 운영할 시 

사용자의 컴퓨터 서비스적 수요가 늘어나면 공급업체에서는 그 서비스를 원활히 운영하기 위해

더 좋은 컴퓨터로 업그레이드하여 서비스적 자원의 공급을 늘렸다.


그 과정에서 공급보다 수요가 많아지면 사용자의 서비스 품질 저하와 공급의 문제점이 발생한다.

또한 수요가 항상 많다가 갑자기 적어질 경우 불필요한 공급으로 인해 공급비용이 낭비되는 문제가 생긴다.


aws 클라우드 컴퓨팅 모델의 경우 변화하는 수요에 따라 탄력적으로 공급이 가능하다.



4. Elastic Ips 


aws에서는 한정된 ip주소를 가지고 있어 ( 0.0.0.0~255.255.255.255 ip주소가 한정적 )

사용자가 running한 인스턴스가 종료하게 되면 할당한 ip주소를  회수해 간다.

다시 인스턴스를 run하면 바뀐 ip주소를 부여해준다.


운영하던 인스턴스를 종료하고 다시 실행했을 시 

기존의 사용하던 ip주소가 바뀌면 문제가 생긴다.


이러한 문제로

사용자는 aws의 Elastic Ips (엘라스틱 아이피)를 할당받아 자신의 인스턴스에 부여하여 연결할 수 있다.


엘라스틱 아이피를 인스턴스에 연결하게 되면 인스턴스를 종료하고 다시 실행 했을 시

ip주소가 바뀌지 않는다.



5. 확장성을 위한 부하 테스트


aws 인스턴스 2대를 running하여

1대는 자신이 운영하고 있는 인스턴스

1대는 공격하려는 인스턴스

로 구성하여


우분투에서 ab -n 400 -c 100 http://ip주소/ 명령어를 사용하여

테스트를 진행한다.(요청은 400 , 동시접속 100 , 접속할 주소 http://)


결과를 통해 자신의 인스턴스를 확장해야 할지 말지 결정할 수 있다.



6. scale-up 방법


스케일 업 하려는 기존의 인스턴스를 이미지로 만들어

확장하려는 유형의 자원을 할당하여 인스턴스를 생성한다.

엘라스틱 아이피를 할당한다.


7. scale-out 방법


판도라는 숲의 특성은 뿌리가 하나여서 숲은 하나의 나무이다.

하나의 뿌리를 공유하고 있는 뿌리가 나무의 개체를 결정한다고 한다면

하나의 거대한 나무가 숲을 이루고 살아간다.


scale-out은 여러대의 컴퓨터가 협력하여 동일목표를 달성을 위해

일하는 컴퓨터들의 사회를 만드는 것이다.


사용자가 많아져 서비스 이용이 많아지면 


web server : 아파치, 엔진엑스

middle ware : php, jsp, spring

database : mysql, oracle

웹서버, 미들웨어, 데이터베이스 에 부하가 생긴다. 


이 부하를 1대의 컴퓨터가 아닌 3대의 컴퓨터로 일을 처리할 수 있고


ELB(elastic load banlancecr 부하분배기)로 여러개 인스턴스를 사용해서 부하를 감당한다.


8. auto scaling


서비스 이용이 많아지면 컴퓨터를 자동으로 생성해서

바로 서비스를 시작하게 했다가


서비스 이용이 적어지면 컴퓨터를 자동으로 삭제해서

과금발생 안하게함


인스턴스의 생성과 삭제를 auto로 반복한다.




출처 : https://opentutorials.org/course/2717/11268






프로젝트에서 sts를 사용해보았는데 익숙하지 않아 

간단하게 mysql을 이용해 data에 접근하는 방법을 알고 싶었다.

다행히도 자세하게 가이드가 된 문서(https://spring.io/guides/gs/accessing-data-mysql/)

가 있어 블로그에 정리하게 되었다.


sts를 이용해 mysql의 data를 접근하는 방법



1. STS 설치 후 패키지 익스플로러로 workspace 생성한다.



2. 다음과 같이 패키지 구조가 생성되었다면



lombok.jar를 다운(https://projectlombok.org/download)

받아 자신이 사용하는 sts나 eclipse 위치를 지정해주고 설치한다.




3. pom.xml 을 열어 다음과 같이 수정한다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
 
    <groupId>org.springframework</groupId>
    <artifactId>gs-mysql-data</artifactId>
    <version>0.1.0</version>
 
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.5.RELEASE</version>
    </parent>
 
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
 
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
 
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
 
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        
        <dependency>
            <groupId>javax.xml.bind</groupId>
            <artifactId>jaxb-api</artifactId>
            <version>2.3.0</version>
        </dependency>        
 
        <dependency>
            <groupId>org.javassist</groupId>
            <artifactId>javassist</artifactId>
            <version>3.23.1-GA</version>
        </dependency>
        <dependency
            <groupId>org.projectlombok</groupId
            <artifactId>lombok</artifactId
            <scope>provided</scope
        </dependency>
    </dependencies>
 
    <properties>
        <java.version>1.8</java.version>
    </properties>
 
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
            <plugin>
                  <groupId>org.apache.maven.plugins</groupId>
                  <artifactId>maven-surefire-plugin</artifactId>
                  <version>2.19.1</version>
            </plugin>            
        </plugins>
        
    </build>
 
</project>
cs


pom.xml 수정이 완료되면 

MySQL 8.0 Command Line Client - Unicode 라는 터미널을 실행시켜

db 루트 비밀번호를 입력하고



아래 커맨드라인을 입력하여 db_example 이라는 db 스키마를 생성하고 user를 만들고 비밀번호와 권한도 준다.


1
2
3
mysql> create database db_example; -- Create the new database
mysql> create user 'springuser'@'%' identified by 'ThePassword'-- Creates the user
mysql> grant all on db_example.* to 'springuser'@'%'-- Gives all the privileges to the new user on the newly created database
cs


4. 다음 hello 패키지 안에 class파일을 생성한다.

총 3개 ( MainController.java, User.java , UserRepository.java)



다음과 같이 구성되었다면 안의 내용을 아래와 같이 넣어준다.


4.1 MainController.java


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package hello;
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
 
@Controller    // This means that this class is a Controller
@RequestMapping(path="/test"// This means URL's start with /demo (after Application path)
public class MainController {
    @Autowired // This means to get the bean called userRepository
               // Which is auto-generated by Spring, we will use it to handle the data
    private UserRepository userRepository;
 
    @GetMapping(path="/add"// Map ONLY GET Requests
    public @ResponseBody String addNewUser (@RequestParam String name
            , @RequestParam String email) {
        // @ResponseBody means the returned String is the response, not a view name
        // @RequestParam means it is a parameter from the GET or POST request
 
        User n = new User();
        n.setName(name);
        n.setEmail(email);
        userRepository.save(n);
        return "Saved";
    }
 
    @GetMapping(path="/all")
    public @ResponseBody Iterable<User> getAllUsers() {
        // This returns a JSON or XML with the users
        return userRepository.findAll();
    }
}
cs


4.2 User.java


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package hello;
 
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import lombok.Data;
 
@Entity // This tells Hibernate to make a table out of this class
@Data
public class User {
    @Id
    @GeneratedValue(strategy=GenerationType.AUTO)
    private Integer id;
    private String name;
    private String email;
 
}
cs


4.3 UserRepository.java


1
2
3
4
5
6
7
8
9
10
11
12
package hello;
 
import org.springframework.data.repository.CrudRepository;
 
import hello.User;
 
// This will be AUTO IMPLEMENTED by Spring into a Bean called userRepository
// CRUD refers Create, Read, Update, Delete
 
public interface UserRepository extends CrudRepository<User, Integer> {
 
}
cs


5. 다음으로 /test/src/main/resources/application.properties 파일을 찾는다.


application.properties 파일은 비어있을텐데 이 부분에 다음과 같이 db정보를 입력해준다.


1
2
3
4
5
#spring.jpa.hibernate.ddl-auto=create
spring.datasource.url=jdbc:mysql://localhost:3306/db_example
spring.datasource.username=springuser
spring.datasource.password=ThePassword
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
cs


6. 패키지를 잡고 다음과 같이 Update Maven Project를 한다.



7. 패키지를 잡고 다음과 같이 Maven install을 한다.




8. 빌드가 성공하면 서버를 올리고 웹브라우저를 열어 http://localhost:8080/test/all 를 입력해본다.


 



mysql workbench 실행하여



UserName을 springuser

password는 ThePassword를 입력하여 접속한다.



데이터가 없는것을 확인했으니 새로 넣어보자.


9. http://localhost:8080/test/add?name=sanghyun&email=shlee0882@gmail.com 을 입력하면



다음과 같이 데이터가 들어가게 된다.



데이터 조회를 해보면 json형식으로 나온것을 확인할 수 있다.



sts와 mysql을 이용하여 데이터 조회(select)와 입력(insert) 기능을 구현하였다.


소스첨부 : 

test.z01

test.z02

test.zip


출처 : https://spring.io/guides/gs/accessing-data-mysql/



+ Recent posts