디자인패턴 Iteratoer Pattern

이터레이터 패턴은 컬렉션 구현 방법을 노출시키지 않으면서도 그 집합체 안에 들어있는 모든 항목에 접근할 수 있게 해주는 방법을 제공해 줍니다.

 

Iteratoer Pattern 어떻게 구현할까요?


이터레이터 패턴은 Iterator라는 인터페이스에 의존합니다.
이터레이터 패턴은 이용하면 내부적인 구현 방법을 외부로 노출시키지 않으면서도 집합체에 있는 모든 항목에 일일이 접근할 수 있습니다.
또한 각 항목에 일일이 접근할 수 있게 해주는 기능을 집합체가 아닌 반복자 객체에서 책임지게 된다는 것도 장점으로 작용합니다.
그러면 집합체 인터페이스 및 구현이 간단해지고, 각자 중요한 일만 처리하면 됩니다.

hasNext와 next라는 함수만 노출시킵니다.
이 함수를 사용하는 사용자는 hasNext가 boolean으로 되어있으니 다음 데이터가 있는지 여부를 반환하는 함수라고 예측 가능하고, Next는 MenuItem이라는 객체를 반환하니 객체에 담겨있는 다음 데이터를 보여주는구나라고 예측할 수 있습니다.
하지만 내부적인 구현 방법은 모릅니다.
이렇게 구현 방법을 외부로 노출시키지 않고 기능을 제공할 수 있습니다.

 

구현

Iterator

package cg.park.designpattern.iterator;

public interface Iterator {
   boolean hasNext();
   MenuItem next();
}

 

WashMenuIterator

package cg.park.designpattern.iterator;

public class WashMenuIterator implements Iterator {
    MenuItem[] items;
    int position = 0;

    public WashMenuIterator(MenuItem[] items) {this.items = items;}

    public MenuItem next() {return items[position++];}
    public boolean hasNext() {return items.length > position;}

}

 

Menu

package cg.park.designpattern.iterator;

public interface Menu {
   public Iterator createIterator();
}

 

WashMenu

package cg.park.designpattern.iterator;

public class WashMenu implements Menu {
    static final int MAX_ITEMS = 6;
    int numberOfItems = 0;
    MenuItem[] menuItems;

    public WashMenu() {
        menuItems = new MenuItem[MAX_ITEMS];
        addItem("카 샴푸");
        addItem("세차용 스폰지");
        addItem("휠 브러시 사용");
        addItem("물통에 스폰지 세척");
        addItem("고압수 뿌리기");
        addItem("타월을 이용하여 물기 제거");
    }

    public void addItem(String description) {
        MenuItem washItem = new MenuItem(description);
        if (numberOfItems >= MAX_ITEMS) {
            System.out.println("최대 개수를 초과했습니다.");
            return;
        }
        menuItems[numberOfItems] = washItem;
        numberOfItems++;
    }

    public MenuItem[] getMenuItems() {
        return menuItems;
    }

    public Iterator createIterator() {
        return new WashMenuIterator(menuItems);
    }

}

 

MenuItem

package cg.park.designpattern.iterator;

public class MenuItem {
    String description;
    public MenuItem(String description) {
        this.description = description;
    }
    public String getDescription() {
        return description;
    }
    public String toString() { return description; }
}

 

Cleaner

package cg.park.designpattern.iterator;

public class Cleaner {
    Menu washMenu;

    public Cleaner(Menu washMenu) {
        this.washMenu = washMenu;
    }

    public void cleaning() {
        System.out.println("==========청소 시작==========");
        printCleaning(washMenu.createIterator());
        System.out.println("==========청소 끝==========");
    }

    private void printCleaning(Iterator iterator) {
        while (iterator.hasNext()) {
            MenuItem menuItem = iterator.next();
            System.out.println(menuItem.description);
        }
    }
}

 

IteratorTest

package cg.park.designpattern.iterator;

public class IteratorTest {
    public static void main(String[] args) {
        Cleaner cleaner = new Cleaner(new WashMenu());
        cleaner.cleaning();
    }
}

 

후기

내부적인 구현 방법을 외부로 노출시키지 않으면서도 집합체에 있는 모든 항목에 일일이 접근할 수 있는 방법은 유용한 기술입니다.

Git: https://github.com/qkrcksrbs8/designpattern

참조 자료: Head First Design Patterns

 

POJO란 무엇인가?

스프링의 목적은 애플리케이션 개발의 복잡함을 줄여주는 것 또는 효과적으로 대응하게 해주는 것입니다.
스프링이 지향하는 목적은 스프링은 엔터프라이즈 서비스 기술과 POJO라는 애플리케이션 로직을 담은 코드를 분리했다는 뜻입니다.
분리됐지만 반드시 필요한 엔터프라이즈 서비스 기술을 POJO방식으로 개발된 애플리케이션 핵심 로직을 담은 코드에 제공한다는 것이 스프링의 가장 강력한 특징과 목표입니다.

 

스프링의 핵심 POJO

스프링은 애플리케이션은 POJO를 이용해서 만든 애플리케이션 코드와, POJO가 어떻게 관계를 맺고 동작하는지를 정의해놓은 설계정보로 구분됩니다.
DI의 기본 아이디어는 유연하게 확장 가능한 오브젝트를 만들어두고 그 관계는 외부에서 다이내믹하게 설정해준다는 것입니다.
이런 DI의 개념을 애플리케이션 전반에 걸쳐 적용하는 것이 스프링의 프로그래밍 모델입니다.
스프링의 주요 기술인 IoC/DI, AOP와 PSA는 애플리케이션을 POJO로 개발할 수 있게 해주는 가능 기술이라고 불립니다.

 

 

POJO란 무엇인가?

POJO는 Plain Old Java Object의 첫 글자를 따서 만든 약자입니다.
최근 몇 년간 자바에서 유행어처럼 사용되고 있는 이 단어는 마틴 파울러가 2000년에 컨퍼런스 발표를 준비하다가 만들어낸 용어라고 합니다.
당시 인기가 있던 EJB는 복잡하고 제한이 많은 기술이었습니다.
EJB를 사용하는 것보다 자바의 단순한 오브젝트를 이용해 애플리케이션의 비즈니스 로직을 구현하는 편이 낫다고 판단한 마틴 파울러는 개발자들이 왜 자바의 단순한 오브젝트를 사용하지 않는지 찾아보았습니다.
이유는 단순히 EJB와 같은 이름이 없어서였습니다.
그래서 POJO라는 이름을 붙인 후 POJO프로그래밍에 대한 개발자들의 관심이 높아졌고 POJO를 지원한다는 걸 장점으로 내세우는 많은 프레임워크와 기술들이 나오기 시작했습니다.

 

POJO의 조건

특정 규약에 종속되지 않는다.
POJO는 자바 언어와 꼭 필요한 API 외에는 종속되지 않아야 합니다.
스트럿츠 1과 같이 특정 클래스를 상속해서 만들어야 하는 규약이 있는 경우에도 POJO가 아닙니다.
스트럿츠는 MVC 패턴에서 Controller 역할을 하는 웹 애플리케이션 프레임워크입니다. 
특정 규약을 따라 만들게 한다면 규약에서 제시하는 특정 클래스를 상속해야 합니다.
그럴 경우 자바의 단일 상속 제한 때문에 더 이상 해당 클래스에 객체지향적인 설계 기법을 적용하기 어려워지는 문제가 생깁니다.
또한 이미 특정 규약에 종속되어 있기 때문에 다른 환경으로 이전이 힘들어지는 문제점이 생깁니다.

 

특정 환경에 종속되지 않는다.

특정 환경에 종속적이어야만 동작하는 오브젝트도 POJO라고 할 수 없습니다.
JNDI가 없는환경에서 그대로 사용하기 힘든 EJB처럼 특정 환경이 의존 대상 검색 방식에 종속적이라면 POJO라고 할 수 없습니다.
비즈니스 로직을 담고 있는 POJO 클래스는 웹이라는 환경정보나 웹 기술을 담고 있는 클래스나 인터페이스를 사용해서는 안 됩니다.
웹이라는 환경으로 제한해버리기 때문입니다.
그리고 웹 서버에 올리지 않으면 독립적으로 테스트하기 어려워집니다.

 

어노테이션을 사용하면?

XML에 담겨있는 설정정보를 자바 코드로 가져왔어도, 그 때문에 환경에 종속되지 않는다면 POJO라고 할 수 있습니다.
하지만 어노테이션이나 엘리먼트 값에 특정 기술과 환경에 종속적인 정보를 담고 있다면 그때는 POJO라고 할 수 없습니다.

 

특정 기술이나 환경에 종속적이지 않다면 모두 POJO일까?

POJO는 객체지향적인 자바 언어의 기본에 충실하게 만들어져야 합니다.
그것이 POJO라는 이름을 붙이면서까지 단순한 자바 오브젝트에 집착하는 이유입니다.
자바 언어와 문법만 사용했다고 해서 객체지향적으로 만들어졌다고 볼 수는 없습니다.
재사용이 불가능할 정도로 다른 레이어와 영역의 코드와 강한 결합을 가지고 만들어진 경우와 OOP를 생각하지 않고 단순 if/switch 문으로 만들었다면, 객체지향적인 자바 오브젝트라고 할 수 없습니다.
이런 식으로 개발이 진행된다면 특정 기술과 환경에 종속적이지 않았지만, POJO라고 부를 수 없습니다.

 

그럼 POJO라고 부를려면 어떻게 해야 할까?

객체지향적인 원리에 충실하면서, 환경과 기술에 종속되지 않고 재사용이 될 수 있는 방식으로 설계된 오브젝트를 말합니다.

POJO의 장점 

POJO의 장점은 POJO조건 그대로가 장점이 됩니다.
특정 기술과 환경에 종속되지 않는 오브젝트는 깔끔한 코드가 될 수 있습니다.
매우 유연한 방식으로 원하는 레벨에서 코드를 빠르고 명확하게 테스트할 수 있습니다.
객체지향적인 설계를 자유롭게 적용할 수 있습니다.

 

POJO 프레임워크

스프링은 POJO를 이용한 엔터프라이즈 애플리케이션 개발을 목적으로 하는 프레임워크라고 했습니다.
POJO 프로그램이 가능하도록 기술적인 기반을 제공하는 프레임워크를 POJO 프레임워크라고 합니다. 
스프링 프레임워크와 하이버네이트가 대표적인 POJO 프레임워크로 볼 수 있습니다.

 

후기

POJO에 나온 특정 기술이나 환경에 종속되지 않게 프로그래밍하는 것은 예전부터 계속 들어왔던 말입니다.
스프링을 사용하고, 더 나은 프로그래밍을 하고싶다면 POJO의 개념을 알고 있을 필요가 있다고 생각합니다.

 

참조 자료: 토비의 스프링 3.1 vol.1

 

디자인패턴 TemplateMethod Pattern

템플릿 메서도 패턴은 메서드에서 알고리즘의 골격을 정의합니다.
알고리즘의 여러 단계 중 일부는 서브클래스에서 구현할 수 있습니다.
템플릿 메소드를 이용하면 알고리즘의 구조는 그대로 유지하면서 서브클래스에서 특정 단계를 재정의 할 수 있습니다.

 

TemplateMethod Pattern 실제로 어떻게 적용할까요?

버거 레시피를 만들 예정입니다.
버거에 들어가는 재료들을 확인하는 기능을 만들겠습니다.

각각의 레시피에 조금 다른 부분이 있긴 하지만, 만드는 방법은 똑같다고 볼 수 있기 때문에, 다른 부분을 추상화 시켜 공유하기로 했습니다.

 

다른 부분을 찾아서 추상화를 했습니다.
이 클래스는 버거 레시피의 알고리즘 틀입니다.

 

알고리즘 틀

템플릿 메소드 패턴은 간단하게 생각하면 알고리즘의 틀을 만들기 위한 패턴입니다.
틀이란 일련의 단계들로 알고리즘을 정의한 메소드입니다.
일련의 단계들 중 하나 이상이 추상 메소드로 정의되며, 그 추상 메소드는 서브클래스에서 구현됩니다.
이렇게 하면 서브클래스에서 달라지는 부분을 구현할 수 있도록 하면서도 알고리즘의 구조는 바꾸지 않아도 됩니다.

 

달라지는 부분은 어떻게 구현할까요?

템플릿 메소드에서는 알고리즘의 각 단계들을 정의하며, 그 중 한 개 이상의 단계가 서브클래스에 의해  제공될 수 있습니다.

버거 레시피 추상클래스가 있고, 그 레시피를 상속받은 치킨버거와 불고기버거 클래스가 있습니다. 
서브클래스를 구현하여 다른 부분에 대해 정의하면 하겠습니다.

 

구현

BurgerBeverage

package cg.park.designpattern.templateMethod;

public abstract class BurgerBeverage {

    final void makeBurger() {
        menu();
        bread();
        patty();
        onion();
        sauce();
        cheese();
    }

    abstract void menu();//메뉴

    abstract void patty();//패티(돼지,닭,소 등...)

    abstract void sauce();//버거 소스

    void bread() { System.out.println("모닝빵");}

    void onion() { System.out.println("양파");}

    void cheese() { System.out.println("치즈");}

}

 

ChickenBurger

package cg.park.designpattern.templateMethod;

public class ChickenBurger extends BurgerBeverage {
    public void menu() { System.out.println("*치킨 버거");}
    public void patty() { System.out.println("치킨 패티");}
    public void sauce() { System.out.println("양념치킨소스");}
}

 

BulgogiBurger

package cg.park.designpattern.templateMethod;

public class BulgogiBurger extends BurgerBeverage {
    public void menu() { System.out.println("*불고기 버거");}
    public void patty() { System.out.println("불고기 패티");}
    public void sauce() { System.out.println("불고기 소스");}
}

 

TemplateMethodTest

package cg.park.designpattern.templateMethod;

public class TemplateMethodTest {
    public static void main(String[] args) {
        BulgogiBurger bulgogiBurger = new BulgogiBurger();
        ChickenBurger chickenBurger = new ChickenBurger();

        bulgogiBurger.makeBurger();
        System.out.println("==========================");
        chickenBurger.makeBurger();

    }
}

 

후기

템플릿 메서드 패턴은 실무에서도 많이 사용되는 패턴입니다.
디자인 패턴에 대해 학습하고, 신규로 접하는 프로젝트가 디자인 패턴이 적용되어 있다면 코드 확장에 필요한 지식이 될 것입니다.

 

Git: https://github.com/qkrcksrbs8/designpattern

참조 자료: Head First Design Patterns

 

스프링의 객체 생성

스프링의 가장 기본적인 기능은 객체를 생성하고 초기화하여 필요로 하는 곳에 제공하는 것인데, 이 중심에는 DI 설계 패턴이 적용되어 있습니다.

 

DI란 무엇일까요?

DI는 Dependency Injection의 약자로서, "의존성 주입"이라는 단어로 번역되어 사용됩니다.
DI는 의존을 처리하는 방법에 대한 내용입니다.
스프링은 기본적으로 DI를 기반으로 동작하기 때문에, 스프링을 제대로 사용하려면 DI에 대한 이해가 필수적입니다.

 

DI의 의존 처리

DI는 의존 객체를 외부에서 조립합니다.
의존 객체를 직접 생상하는 방식과 달리 DI(Dependency Injection)는 의존 객체를 외부로부터 전달받는 구현 방식입니다.
생성자를 이용해서 의존 객체를 전달받는 방식이 DI에 따라 구현한 것이기 때문입니다.

햄버거를 만드는 클래스가 있습니다.
치즈버거를 객체를 생성했습니다.
그럼 다른 버거는 어떻게 처리해야 할까요?
의존 객체를 내부에서 처리한다면 버거의 종류가 바뀔 때마다 클래스의 수정이 필요합니다.

public class BurgerStore {
    BurgerFactory factory = new CheeseBurger();//치즈버거 주문
    //BurgerFactory factory = new BulgogiBurger();//불고기버거 주문
    //BurgerFactory factory = new ChickenBurger();//치즈버거 주문
}

위의 상황처럼 매번 수정이 필요하다면 매우 불편할 것입니다.
저렇게 고정되어 있는 메뉴를 생성하는 것이 아닌 주문받은 메뉴를 생성하게 된다면 편리할 것입니다.

 

public class BurgerStore {
    BurgerFactory factory;

    public BurgerStore(BurgerFactory factory) {
        this.factory = factory;
    }
}

고정 되어있는 객체생성 방법이 아닌 주입받은 객체로 생성하는 방법입니다.
new CheeseBurger()로 선언되어있지 않고, 주입받는 Burger 객체로 생성이 됩니다.

 

내부에서 객체를 생성

 

외부에서 주입하여 객체 생성

 

후기

DI(Dependency Injection)는 스프링 특징 중 하나입니다.
스프링을 사용한다면 DI(Dependency Injection)는 필수로 알아야 합니다.
필수인 이유는 모든 개발자가 추구하는 결합도가 낮고, 응집도가 높은 구현 방법이기 때문입니다.
물론 DI(Dependency Injection)개념을 알고있다고 해서 모든것이 해결되는 것은 아닙니다.
그래도 스프링을 사용한다면 스프링의 특징과 사용법을 알면 더 나은 결과를 만들 수 있습니다.
감사합니다.

 

참조 자료: 웹 개발자를 위한 Spring 4.0 프로그래밍

참조 자료: 토비의 스프링 3.1 vol2

'스프링 > Spring' 카테고리의 다른 글

[Spring] POJO란 무엇인가?  (0) 2022.01.17
[스프링] 트랜잭션이란?  (0) 2022.01.12
[Spring] 스프링이란 무엇인가?  (0) 2022.01.10
[스프링] 기본 흐름과 주요 컴포넌트  (0) 2022.01.02

 

디자인패턴 Facade Pattern

퍼사드 클래스에서는 서브시스템 클래스들을 캡슐화하지 않습니다.
그냥 서브시스템의 기능을 사용할 수 있는 간단한 인터페이스를 제공할 뿐입니다.

 

Facade Pattern은 실제로 어떻게 적용할까요?

어떤 일을 하기 위해서는 작업 순서가 있습니다.
하지만 그 작업 순서를 하나씩 동작하기엔 너무 복잡하지 않을까요?
복잡한 작업 순서들을 하나로 묶어서 사용할 수 있게 된다면 편리하지 않을까요?
이런 경우에 퍼사드 패턴을 사용하면 됩니다.
퍼사드 클래스를 구현함으로써 복잡한 시스템을 훨씬 쉽게 사용할 수 있습니다.
퍼사드는 인터페이스를 단순화시킬 뿐 아니라 클라이언트와 구성요소들로 이루어진 서브시스템을 분리시키는 역할도 합니다.

 

세차하는 시스템을 퍼사드로 구현

세차를 할 때 작업 순서가 있습니다.
 -카 샴푸 사용
 -세차용 스펀지로 닦기
 -물통에 세차용 스펀지 세척하기
 -휠 브러시 사용하기
 -고압수 뿌리기
 -타월을 사용하여 물기 제거하기

 

 

이 작업 순서를 매번 하나씩 동작한다면 번거롭기도 하고, 복잡하기도 합니다.
더 간편한 방법이 있을지 찾아봅니다.
그래서 위의 작업 순서들을 하나로 묶기로 했습니다.

 

세차를 할 때 복잡하던 작업 순서들을 하나로 묶었습니다.
기존에는 각 클래스를 하나씩 동작해야 했다면, 이제는 CarWash라는 클래스로 기능들을 묶어서 한 번만 사용하면 됩니다.

 

구현

BubbleBomb

package cg.park.designpattern.facade;

public class BubbleBomb {
    String description;
    public BubbleBomb(String description) {
        this.description = description;
    }

    public void on() {System.out.println(description + " 샴푸 on");}

    public void off() {
        System.out.println(description + " 샴푸 off");
    }

}

 

WashMitt

package cg.park.designpattern.facade;

public class WashMitt {
    String description;

    public WashMitt(String description) {
        this.description = description;
    }

    public void on() {
        System.out.println(description + " 세차용 스폰지 on");
    }

    public void off() {
        System.out.println(description + " 세차용 스폰지 off");
    }

}

 

WheelBrush

package cg.park.designpattern.facade;

public class WheelBrush {
    String description;

    public WheelBrush(String description) {
        this.description = description;
    }

    public void on() {
        System.out.println(description + " 휠 브러시 on");
    }

    public void off() {
        System.out.println(description + " 휠 브러시 off");
    }

}

 

Bucket

package cg.park.designpattern.facade;

public class Bucket {
    String description;

    public Bucket(String description) {
        this.description = description;
    }

    public void on() {
        System.out.println(description + " 물통에 스폰지 세척 on");
    }

    public void off() {
        System.out.println(description + " 물통에 스폰지 세척 off");
    }
}


PressureWasher

package cg.park.designpattern.facade;

public class PressureWasher {
    String description;

    public PressureWasher(String description) {
        this.description = description;
    }

    public void on() {
        System.out.println(description + " 고압수 뿌리기 on");
    }

    public void off() {
        System.out.println(description + " 고압수 뿌리기 off");
    }

}

 

Towel

package cg.park.designpattern.facade;

public class Towel {
    String description;

    public Towel(String description) {
        this.description = description;
    }

    public void on() {
        System.out.println(description + " 타월로 물기 제거 on");
    }

    public void off() {
        System.out.println(description + " 타월로 물기 제거 off");
    }

}

 

CarWash

package cg.park.designpattern.facade;

public class CarWash {
    BubbleBomb bubbleBomb;//카 샴푸
    Bucket bucket;//물통
    PressureWasher pressureWasher;//고압 세척기
    Towel towel;//타월
    WashMitt washMitt;//세차용 스폰지
    WheelBrush wheelBrush;//휠 브러시

    public CarWash(
           BubbleBomb bubbleBomb
         , Bucket bucket
         , PressureWasher pressureWasher
         , Towel towel
         , WashMitt washMitt
         , WheelBrush wheelBrush) {

        this.bucket = bucket;
        this.washMitt = washMitt;
        this.wheelBrush = wheelBrush;
        this.bubbleBomb = bubbleBomb;
        this.towel = towel;
        this.pressureWasher = pressureWasher;
    }

    public void start() {
        bubbleBomb.on();//카 샴푸
        washMitt.on();//세차용 스폰지
        wheelBrush.on();//휠 브러시
        bucket.on();//물통
        pressureWasher.on();//고압수 뿌리기
        towel.on();//타월을 이용하여 물기 제거
    }

    public void end() {
        bubbleBomb.off();
        wheelBrush.off();
        washMitt.off();
        bucket.off();
        pressureWasher.off();
        towel.off();
    }

}

 

FacadeTest

package cg.park.designpattern.facade;

public class FacadeTest {

    public static void main(String[] args) {
        BubbleBomb bubbleBomb = new BubbleBomb("A사 제품");
        Bucket bucket = new Bucket("A사 제품");
        PressureWasher pressureWasher = new PressureWasher("A사 제품");
        Towel towel = new Towel("A사 제품");
        WashMitt washMitt = new WashMitt("A사 제품");
        WheelBrush wheelBrush = new WheelBrush("A사 제품");

        CarWash carWash = new CarWash(
                bubbleBomb
                , bucket
                , pressureWasher
                , towel
                , washMitt
                , wheelBrush);

        carWash.start();//세차 시작
        System.out.println("=================================");
        carWash.end();//세차 종료

    }

}

 

후기

처음에는 퍼사드패턴과 커맨드 패턴이 헷갈렸습니다.
퍼사드 패턴과 커맨드 패턴 둘 다 공부하니 개념적인 차이를 이해했습니다.
퍼사드 패턴: 서브클래스들을 캡슐화하지 않고 구현,  훨씬 쉽게 사용할 수 있는 인터페이스를 제공함으로써 복잡한 시스템을 쉽게 사용.
커맨드 패턴: 요구사항을 객체로 캡슐화, 매개변수로 다른 여러 가지 요구사항 추가 가능.

커맨드 패턴 보러가기

 

Git: https://github.com/qkrcksrbs8/designpattern

참조 자료: Head First Design Patterns

 

트랜잭션의 필요성

NoSQL과 빅데이터가 급부상하면서 RDBMS가 제공하는 엄격한 트랜잭션보다는 대용량 데이터 처리를 위한 느슨한 방식의 무결성 처리 기법을 적용하는 곳이 증가하고 있습니다.
하지만, 그럼에도 불구하고 전통적인 RDBMS가 제공하는 트랜잭션은 중요합니다.
예를 들어, 결제와 영화예매하는 시스템이 있습니다.
만약 이 시스템에서 결제는 이루어진 상태에서 영화 예매에 실패하는 상황이 발생하면 당연히 결제에 성공하면 안됩니다.
이런 경우 결제와 영화예매 모두 실패로 처리되어야 합니다.

 

트랜잭션이란?

트랜잭선은 여러 과정을 하나의 동작으로 묶을 때 사용됩니다.
트랜잭선 범위 내에 있는 처리 과정 중 한 가지라도 실패할 경우 전체 과정을 취소시킴으로써 데이터의 무결성을 보장합니다.
이러한 트랜잭션의 무결성 특징을 ACID라고 말합니다.
트랜잭션 과정 중 하나라도 실패한다면 모든 과정은 롤백됩니다.

 

롤백

데이터베이스에서 업데이트에 오류가 발생할 때, 이전 상태로 되돌리는 것을 말합니다.

 

ACID란 무엇일까요?

원자성(Atomicity)
트랜잭션은 한 개 이상의 동작을 논리적으로 한 개의 작업 단위로 묶습니다.
원자성은 트랜잭션 범위에 있는 동작이 모두 실행되거나 모두 실행되지 않음을 보장합니다.
모든 동작이 성공적으로 실행되면 트랜잭션은 성공입니다.
하나라도 실패한다면 트랜잭션은 실패하고 모든 과정은 롤백합니다.

 

일관성(Consistency)
트랜잭션은 항상 일괄적인 DB상태를 유지해야 합니다.
정의된 조건과 다른 결과가 나온다면 일관성이 지켜졌다고 말할 수 없습니다.
일관성이 지켜지지 않았다면 트랜잭션은 실패한 것이고, 모든 과정은 롤백합니다.

 

고립성(Isolation)
트랜잭션은다른 트랜잭션과 독립적으로 실행되어야 하며, 다른 트랜잭션이 동일한 데이터에 동시에 접근할 경우 접근을 제어해야 합니다.

 

지속성(Durability)
트랜잭션이 완료되면, 그 결과는 지속적으로 유지되어야 합니다.
DB에 반영된 데이터가 불특정하게 변경된다면, 지속성이 지켜졌다고 말할 수 없습니다.
이 경우에도 트랜잭션은 실패한 것이고, 모든 과정은 롤백합니다.

 

스프링의 트랜잭션 지원

스프링은 코드 기반의 트랜잭션 처리(Programmatic Transaction) 뿐 아니라 선언적 트랜잭션(Declarative Transaction)을 지원하고 있습니다.
개발자가 직접적으로 트랜잭션의 범위를 코드 수준에서 정의하고 싶은 경우에는 스프링이 제공하는 트랜잭션 템플릿 클래스를 이용해서 트랜잭션 범위를 지정할 수 있습니다.
또한, 설정 파일이나 어노테이션을 이용하여 트랜잭션 범위 및 규칙을 정의할 수 있기 때문에 트랜잭션을 매우 쉽게 관리할 수 있습니다.

 

트랜잭션 전파와 관련해서 스프링이 지원하는 속성

REQUIRED

메서드를 수행하는 데 트랜잭션이 필요하다는 것을 의미합니다.
현재 진행 중인 트랜잭션이 존재하면, 해당 트랜잭션을 사용합니다.
존재하지 않는다면 새로운 트랜잭션을 생성합니다.

 

MANDATORY

메서드를 수행하는 데 트랜잭션이 필요하다는 것을 의미합니다.
하지만, REQUIRED와 달리, 진행 중인 트랜잭션이 존재하지 않을 경우 익셉션을 발생시킵니다.

 

REQUIRES_NEW

항상 새로운 트랜잭션을 시작합니다.
기존 트랜잭션이 존재하면 기존 트랜잭션을 일시 중지하고 새로운 트랜잭션을 시작합니다.
새로 시작된 트랜잭션이 종료된 뒤에 기존 트랜잭션이 계속됩니다.

 

SUPPORTS

메서드가 트랜잭션을 필요로 하지 않지만, 기존 트랜잭션이 존재할 경우 트랜잭션을 사용한다는 것을 의미합니다.
진행 중인 트랜잭션이 존재하지 않더라도 메서드는 정상적으로 동작합니다.

 

NOT_SUPPORTED

메서드가 트랜잭션을 필요로 하지 않음을 의미합니다.
SUPPORTS와 달리 진행 중인 트랜잭션이 존재할 경우 메서드가 실행되는 동안 트랜잭션은 일시 중지되며, 메서드 실행이 종료된 후에 트랜잭션을 계속 진행합니다.

 

NEVER

메서드가 트랜잭션을 필요로 하지 않으며, 만약 진행 중인 트랜잭션이 존재하면 익셉션을 발생시킵니다.

 

NESTED

기존 트랜잭션이 존재하면, 기존 트랜잭션에 중첩된 트랜잭션에서 메서드를 실행합니다.
기존 트랜잭션이 존재하지 않으면 REQUIRED와 동일하게 동작합니다.
이 기능은 JDBC 3.0 드라이버를 사용할 때에만 적용됩니다.

 

트랜잭션 격리 레벨

DEFAULT

기본 설정을 사용합니다.

 

READ_UNCOMMITTED

다른 트랜잭션에서 커밋하지 않은 데이터를 읽을 수 있습니다.

 

READ_COMMITTED

다른 트랜잭션에 의해 커밋된 데이터를 읽을 수 있습니다.

 

REPEATABLE_READ

처음에 읽어 온 데이터와 두 번째 읽어 온 데이터가 동일한 값을 갖습니다.

 

SERIALIZABLE

동일한 데이터에 대해서 동시에 두 개 이상의 트랜잭션이 수행될 수 없습니다.

 

후기

스프링의 트랜잭션은 개념 뿐 아니라 내부적으로 어떻게 돌아가는지 공부할 필요가 있습니다.

 

참조 자료: 웹 개발자를 위한 Spring 4.0 프로그래밍

 

디자인패턴 Adapter Pattern

클래스의 인터페이스를 클라이언트에서 요구하는 다른 인터페이스로 변환합니다.
인터페이스가 호환되지 않아 쓸 수 없었던 클래스들을 같이 사용할 수 있게 해줍니다.

 

어댑터란?

어댑터는 그리 어렵지 않게 이해할 수 있습니다.
우리 주변에서 어댑터를 쉽게 볼 수 있습니다.
예를 들어, 한국에서 쓰던 220볼트 어댑터를 해외에 가서 사용하려면 어떻게 해야할까요?
같은 220볼트를 사용하는 국가라면 괜찮지만, 그렇지 않다면?

다들 어댑터가 어떻게 사용되고 있는지 알고 있습니다.
다른 부분을 변환하여 연결시키는 중간역할입니다.

 

 

어댑터 패턴은 실제로 어떻게 적용할까요?

기존 시스템과 신규 시스템이 호환되지 않는다면?

 

어댑터의 역할을 여기서 확인할 수 있습니다.
그림만 봐도 느낌이 오지 않나요?

 

어댑터가 있다면 다른 부분을 변환하여 연결시키는 중간역할을 할 수 있습니다.

 

구현

한국에서 사용하는 220볼트 플러그와 일본에서 사용하는 110볼트 플러그가 있습니다.
220볼트를 어댑터를 사용하여 110볼트를 사용할 수 있게 만들고,
110볼트도 어댑터를 사용하여 220볼트를 사용할 수 있게 만들 예정입니다.

 

Volt220

package cg.park.designpattern.adapter;

public interface Volt220 {
    public void plugIn220();
}

 

usedInKorea

package cg.park.designpattern.adapter;

public class usedInKorea implements Volt220 {
    public void plugIn220() { System.out.println("220볼트 사용");}
}

 

Volt110

package cg.park.designpattern.adapter;

public interface Volt110 {
    public void plugIn110();
}

 

usedInJapan

package cg.park.designpattern.adapter;

public class usedInJapan implements Volt110 {
    public void plugIn110() { System.out.println("110볼트 사용");}
}

 

VoltAdapter220

package cg.park.designpattern.adapter;

public class VoltAdapter220 implements Volt110 {
    Volt220 volt220;

    public VoltAdapter220(Volt220 volt220) {
        this.volt220 = volt220;
    }

    public void plugIn110() {
        volt220.plugIn220();
    }
}

 

VoltAdapter110

package cg.park.designpattern.adapter;

public class VoltAdapter110  implements Volt220 {
    Volt110 volt110;

    public VoltAdapter110(Volt110 volt110) {
        this.volt110 = volt110;
    }

    public void plugIn220() {
        volt110.plugIn110();
    }
}

 

AdapterTest

package cg.park.designpattern.adapter;

public class AdapterTest {
    public static void main(String[] args) {
        Volt110 volt110 = new VoltAdapter220(new usedInKorea());
        Volt220 volt220 = new VoltAdapter110(new usedInJapan());

        volt110.plugIn110();//volt110 사용
        volt220.plugIn220();//volt220 사용

    }

}

 

110볼트를 220볼트 어댑터로 감싸고, 

220볼트를 110볼트 어댑터로 감싼 후

실행하게 되면 위와 같이 결과가 나옵니다.

220어댑터(110볼트): 220볼트 사용

110어댑터(220볼트): 110볼트 사용

 

후기

어댑터의 개념은 많이 사용된다.

코드를 고치지 않고 새로운 라이브러리를 적용할 수 있다는 것은 큰 장점이다.

 

Git: https://github.com/qkrcksrbs8/designpattern

참조 자료: Head First Design Patterns

 

스프링의 유래

'스프링'이라는 이름의 유래는 이전에 Java EE(엔터프라이즈 에디션)의 스펙을 구현한 EJB가 있었습니다.
EJB 스펙을 따르는 오브젝트들은 객체지향적인 특징과 장점을 포기해야 했습니다.
EJB는 상속과 다형성 등의 혜택을 제대로 누릴 수 없었습니다.
그럼에도 EJB가 계속 사용되었던 이유는 엔터프라이즈 애플리케이션에서 반드시 필요로 하는 주요한 엔터프라이즈 서비스들을 애플리케이션 코드와 분리해서 독립적인 서비스로 사용할 수 있게 만들어졌다는 점입니다.
EJB가 기술의 복잡도가 증가해서 성능이 느렸던 것을 탈피하여, EJB 시절을 “겨울”에 빗대어 겨울 후의 “봄”으로 새로운 시작한다는 것을 의미하는 Spring(봄)이 되었습니다.

 

Spring이란 무엇인가?

Spring은 Framework입니다.
Framework를 그대로 번역하면 뼈대라는 뜻입니다.
컴퓨터 프로그래밍에서는 복잡한 문제를 해결하거나 서술하는 데 사용되는 기본 개념 구조입니다.
Spring Framework는 Java 애플리케이션 개발을 위한 포괄적인 인프라 지원을 제공하는 Java 플랫폼입니다. 
Spring이 인프라를 처리하므로 애플리케이션에 집중할 수 있습니다.
Spring을 사용하면 "plain old Java objects"(POJO)에서 애플리케이션을 빌드하고 POJO에 엔터프라이즈 서비스를 침해하지 않고 적용할 수 있습니다. 
이 기능은 Java SE 프로그래밍 모델과 전체 및 부분 Java EE에 적용됩니다.

 

프레임워크 모듈

Spring Framework는 약 20개의 모듈로 구성된 기능으로 구성되어 있습니다. 
이러한 모듈은 다음 다이어그램과 같이 그룹화됩니다.

Core Container
Data Access/Integration
Web, AOP(Aspect Oriented Programming)
Instrumentation
Messaging
Test

 

 

Spring 주요 특징

POJO(Plain Old Java Object) 방식

POJO는 Java EE의 EJB 를 사용하면서 해당 플랫폼에 종속되어 있는 무거운 객체들을 만드는 것에 반발하며 나타난 용어다. 별도의 프레임워크 없이 Java EE를 사용할 때에 비해 특정 인터페이스를 직접 구현하거나 상속받을 필요가 없어 기존 라이브러리를 지원하기가 용이하고, 객체가 가볍습니다.

 

관점 지향 프로그래밍(Aspect Oriented Programming, AOP)

컴퓨팅에서 관점 지향 프로그래밍(aspect-oriented programming, AOP)은 횡단 관심사(cross-cutting concern)의 분리를 허용함으로써 모듈성을 증가시키는 것이 목적인 프로그래밍 패러다임입니다.
코드 그 자체를 수정하지 않는 대신 기존의 코드에 추가 동작(어드바이스)을 추가함으로써 수행하며, "함수의 이름이 'set'으로 시작하면 모든 함수 호출을 기록한다"와 같이 어느 코드가 포인트 컷(pointcut) 사양을 통해 수정되는지를 따로 지정합니다.
이를 통해 기능의 코드 핵심부를 어수선하게 채우지 않고도 비즈니스 로직에 핵심적이지 않은 동작들을 프로그램에 추가할 수 있게 합니다.
관점 지향 프로그래밍은 관점 지향 소프트웨어 개발의 토대를 형성합니다.

AOP에 대해 더 알아보기

 

의존성 주입(Dependency Injection, DI)

프로그래밍에서 구성요소 간의 의존 관계가 소스코드 내부가 아닌 외부에서 설정을 통해 정의되는 방식입니다.
코드 재사용을 높여 소스코드를 다양한 곳에 사용할 수 있으며 모듈간의 결합도도 낮출 수 있습니다.
계층, 서비스 간에 의존성이 존재하는 경우 스프링 프레임워크가 서로 연결시켜줍니다.

 

제어 역전(Inversion of Control, IoC)

전통적인 프로그래밍에서는 개발자가 작성한 프로그램이 외부 라이브러리의 코드를 호출해서 이용했습니다.
제어 역전은 이와 반대로 외부 라이브러리 코드가 개발자의 코드를 호출하게 됩니다.
즉, 제어권이 프레임워크에게 있어 필요에 따라 스프링 프레임워크가 사용자의 코드를 호출합니다.

 

생명주기 관리

스프링 프레임워크는 Java 객체의 생성, 소멸을 직접 관리하며 필요한 객체만 사용할 수 있습니다.

 

후기

백엔드 개발자를 하게 된다면 스프링은 들어볼 수밖에 없습니다.
하지만 스프링을 사용하는 이유를 잘 모르는 사람도 많습니다.
이미 큰 규모의 인프라가 구축되어 있어서 스프링 시장이 잘 죽지 않고 다른 언어에 비해 조만간 사라질 언어는 아니다 라는 느낌으로 사용하는 분들도 있습니다.
틀린 말은 아닙니다.
하지만 스프링이 왜 탄생하게 되었고, 그래서 다른 프레임워크들과 비교했을 때 장점이 무엇인지 알게 된다면 스프링의 특징을 잘 활용할 수 있다고 생각합니다.

 

참조 자료: Spring IO 페이지

참조 자료: Spring 나무위키

+ Recent posts