반응형
이 내용은 디자인패턴 스터디를 준비하며 정리한 내용이다. 첫 시간에 팩토리패턴을 맡게 되어 정리해보았다.

먼저, 디자인 패턴에는 포함되지 않지만 팩토리 구조에 대해 먼저 이해해보는 시간을 가져보자.

팩토리를 사용하는 이유

클라이언트에서 사용할 객체를 생성하는 부분을 캡슐화하여 느슨한 결합 상태로 만들어, 변화에는 닫혀있고 확장에는 열려있는 코드를 만들 수 있다.

 Duck duck;

 if ( type == picnic ) duck = new MallardDuck();

 else if ( type == hunting ) duck = new DecoyDuck();

 else if ( type == inBathTub) duck = new RubberDuck();


뭔가 변경하거나 확장해야 할 때 코드를 다시 확인하고 추가 또는 제거해야 한다는 것을 의미함.
구상클래스를 많이 사용하면 새로운 요구사항이 생길때마다 코드를 고쳐야 하기때문에 많은 문제가 생긴다. 

- 이 문제를 해결하기 위해 자주 바뀌는 코드를 바뀌지 않는 코드와 분리 시켜야한다.

 Pizza orderPizza(String type) {

       Pizza pizza;

         

     if(type.equals("cheese")) pizza = new CheesePizza();

       else if(type.equals("greek")) pizza = new GreekPizza();

       else if(type.equals("pepperoni")) pizza = new PepperoniPizza();       

  

       pizza.prepare();

       pizza.bake();

       pizza.cut();

       pizza.box();

       return pizza;

 } 


굵게 표시된 부분을 캡슐화 해보자. 아래 예제 처럼 일반적으로 팩토리라 하면은 객체 생성을 처리하는클래스로 사용된다. 

 public class SimplePizzaFactory {

    public Pizza createPizza(String type){ //이런 경우에는 static메소드로 선언하는 경우가 종종 있음.

         Pizza pizza = null;

         if(pizza.equals("cheese")) pizza = new CheesePizza();

         if(pizza.equals("pepper")) pizza = new PepperoniPizza();

         if(pizza.equals("clam")) pizza = new ClamPizza();

         if(pizza.equals("veggie")) pizza = new VeggiePizza();

         return pizza;

    }

 }


생성을 한군데 에서 관리할 수 있다는 이점이 있다. 이것은 팩토리 패턴이라기 보다 팩토리라 불리는 관용구에 가깝다.
팩토리의 정의는 객체 생성 처리 클래스이다. 하지만 이러한 팩토리만 이용한다고 해서 팩토리 패턴이라 부를 수 없다.

팩토리 메소드 패턴(Factory Method Pattern)

객체를 만들어내는 부분을 서브클래스에 위임하는 패턴이다. 이를 위해서는 다음과 같은 조건이 필요하다.

첫째, 객체 생성을 위한 인터페이스를 정의하여야 하고, 

생산자 클래스는 서브클래스로 인해 성질이 정의되어야한다.

또한, 팩토리에서 객체를 생성하여 제품 클래스에서 실제로 재작한다.


메인 프로그램에서 new 키워드가 없다. 객체 생성을 팩토리 클래스에 위임하였기 때문이다.

팩토리 패턴을 사용하는 이유는 클래스간 결합도를 낮추기 위해서 즉,  변경점이 생겼을때 다른 클래스의 영향을 최소화 하기 위해서이다.팩토리 메소드 패턴을 사용하는 경우 직접 객체를 생성해 사용하는 것을 방지하고 서브 클래스에 위임함으로써 보다 효율적인 코드 제어를 할 수 있고 의존성을 제거한다.


* 다이어그램



product 인터페이스 : 생성할 대상의 클래스를 추상화시킨 인터페이스. 생성 대상이 되는 구상 클래스는 이 인터페이스를 구현해야 한다.
Creator: product를 생성하기 위한 기능이 정의되어있는 추상클래스(또는 인터페이스), product를 만드는 메소드는 구현이 없는 추상메소드로 정의해여야한다. 팩토리의 역할을 가진 모든 클래스는 이 인터페이스를 구현해야 한다.

이러한 구조를 갖기 때문에 Product를 생성하는 factoryMethod()는 Creator의 구상클래스에서 구현한다.
Product를 만드는 방법은 Creator의 구상클래스가 구현하므로 생성 대상의 구상 클래스를 만들어내는 작업은 해당 클래스에서 책임지며 추후 변경사항이 생겨도 이 클래스만 변경한다.






출처: http://jusungpark.tistory.com/14 [정리정리정리]
https://cheolhojung.github.io/2018/03/21/%ED%8C%A9%ED%86%A0%EB%A6%AC-%EB%A9%94%EC%86%8C%EB%93%9C-%ED%8C%A8%ED%84%B4/


반응형

'개발공부 > 디자인패턴' 카테고리의 다른 글

디자인패턴  (0) 2021.01.14
반응형
String
String은 새로운 값을 할당할 때마다 새로 생성된다.
char형의 배열형태로 저장되며 final형이기 때문에 초기값으로 주어진 String의 값은 불변으로 바꿀 수가 없게 되는 것이다.

StringBuilder, StringBuffer

둘 다 객체의 공간이 부족해지면 기존의 버퍼 크기를 늘리면서 유연하게 동작한다.

StringBuffer는 멀티스레드 환경에서의 동기화를 지원하지만 StringBuilder는 단일스레드 환경에서만 동기화를 지원한다. 물론 단일스레드 환경에서 StringBuffer를 사용해서 문제가 되는 것은 아니지만 동기화와 관련된 처리로 인해 StringBuilder에 비해 성능이 좋지 않다.

StringBuffer는 각 메서드별로 Synchronized Keyword가 존재하여, 멀티스레드 환경에서도 동기화를 지원.


※ JDK 1.5 버전 이후에는 String 객체를 사용하더라도 컴파일 단계에서 StringBuilder로 컴파일되도록 변경되었다. 따라서 일반적으로 String 클래스를 활용해도 StringBuilder와 성능상으로 차이가 없다고 한다.


하지만 반복 루프를 사용해서 문자열을 더할 때에는 객체를 계속 추가한다는 사실에는 변함이 없습니다. 그러므로 String 클래스를 쓰는 대신, 스레드와 관련이 있으면 StringBuffer를, 스레드 안전 여부와 상관이 없으면 StringBuilder를 사용하는 것을 권장합니다.

 

단순히 성능만 놓고 본다면 연산이 많은 경우, StringBuilder > StringBuffer >>> String 입니다.


검색을 통하여

출처: http://12bme.tistory.com/42 [길은 가면, 뒤에 있다]


반응형
반응형
JAVA8에 새롭게 추가된 것을 고르자면 람다를 고를 수 있다. 무조건 람다식을 공부해야 하는것이 아닌 람다의 근간이 되는 함수형 프로그래밍을 공부하여야 한다. JAVA에서 함수형 프로그래밍의 개념을 찾아볼 수 있는것이 바로 람다식이라고 한다.

그럼 함수형프로그래밍이란 무엇인지부터 살펴보자.

1. 함수형 프로그래밍이란?

먼저 이야기 하기 전에 다음과 같은 코드를 보자

take(10, print(number))

take 함수의 두번째 매게변수로 함수가 넘어가고 있다. JAVA에서는 사실 컴파일에러를 내뱉을 것이다. JAVA는 함수를 매개변수로 갖는것이 불가능 했다. 자바의 메소드는 일급 함수가 아니므로, 다른 메소드로 전달할 수 없다. 

일급함수(일급객체)의 특성을 간단히 정리한다면

1. 변수나 데이터 구조안에 담을 수 있다.
2. 파라미터로 전달할 수 있다.
3. 반환값으로 사용할 수 있다.
4. 할당에 사용된 이름과 상관없이 고유한 구별이 가능하다.

람다는 특정 클래스에 종속되지 않고 메서드처럼 파라미터리스트, 바디, 반환 형식등을 포함한다. 
Java8 에서 함수형 인터페이스(단 하나의 메소드만이 선언된 인터페이스)라는 개념을 도입하게 되었고, 함수형 인터페이스의 경우, 람다식으로 표현이 가능할 수 있게 제공하였다.

람다식의 기본적인 문법은 다음과 같다.

(arg1, arg2...) - > {body}
-> 을 기준으로 왼쪽은 파라미터 리스트 오른쪽은 행위이다. 

정리해보자면 아래와 같다.

  • 단순한 람다 구문의 경우, 람다 구분에 중괄호가 없을 수도 있다.

  • return 이 없을 수도 있다.

  • 매개변수에는 타입을 명시하지 않아도 된다. (타입 추론)

  • 람다식 문법을 컴파일러가 익명 클래스로 변환한다. 즉, 함수형 인터페이스를 컴파일러가 구현하도록 위임하는 형태라 볼 수 있다


단순히 문법만 짧아진것이 장점이 아니라 행위를 파라미터로 전달하므로서 더욱 유연하고 재사용 가능한 API를 만들 수 있게 되었다.

이제는 코드를 같이 보면서 람다에 대해 이야기 해보자.


람다 표현식은 구현해야될 추상 메서드가 1개인 인터페이스를 구현한 것이라고 앞서 이야기 하였다. 추후 유지보수를 할때 한개의 메소드를 추가하려하면 람다의 조건은 깨지므로 나머지 코드가 오류가 날것이다. 이를 방지하기 위한 어노테이션이 바로 @FunctionalInterface 이다.

/**
*해당 인터페이스가 함수형인터페이스라는것을 알려주고
*한개 초과의 메서드를 작성할 시 컴파일러 에러를 낸다.
*/
@FunctionalInterface
public interface Movable {
void move(String str);
}


또한, 함수형 프로그래밍에서 함수는 인풋에 의해서만 아웃풋이 달라져야하며 그것을 지원하기위해 람다 표현식으로 구현할때 객체는 상태를 가질 수 없다. 따라서 인스턴스 필드가 들어갈 수 없다.

2. 함수의 파라미터화
앞서 파라미터로 행위를 전달하는것이라고 했다.  자세한 내용을 다음 소스를 통해 알아보자.

public class Fruit {
private String name;
private String color;

Fruit(String name, String color){
this.name = name;
this.color = color;
}

String getName(){
return this.name;
}

String getColor(){
return this.color;
}
}




List<Fruit> extractApple(List<Fruit> fruits){
List<Fruit> resultList = new ArrayList<>();
        for(Fruit fruit : fruits){
            if("apple".equals(fruit.getName())){
             resultList.add(fruit);
        }
    }

         return resultList;
}

    List<Fruit> extractRed(List<Fruit> fruits){
    List<Fruit> resultList = new ArrayList<>();
         for(Fruit fruit : fruits){
            if("red".equals(fruit.getColor())){
             resultList.add(fruit);
        }
    }

    return resultList;
}


소스를 살펴보면 if문 외에는 전부 동일하다. 이걸 행위 파라미터를 이용해 1개의 메서드로 합치면 훨씬 보기가 좋을것 같다.

static List<Fruit> extractFruitList(List<Fruit> fruits, Predicate<Fruit> predicate){
    List<Fruit> resultList = new ArrayList<>();
    for(Fruit fruit : fruits){
        if(predicate.test(fruit)){
            resultList.add(fruit);
        }
    }

 

    return resultList;
}

호출해보자. 
List<Fruit> fruits = Arrays.asList(new Fruit("apple", "red"), new Fruit("melon", "green"), new Fruit("banana", "yellow"));

List<Fruit> appleList = extractFruitList(fruits, new Predicate<Fruit>() {
      @Override
      public boolean test(Fruit fruit) {
          return "apple".equals(fruit.getName());
      }
  });

List<Fruit> redList = extractFruitList(fruits, new Predicate<Fruit>() {
      @Override
      public boolean test(Fruit fruit) {
          return "red".equals(fruit.getColor());
      }
  });

Predicate 인터페이스는 추상메서드가 한개 뿐이므로 람다식 적용가능 하다. 각각에 람다를 적용한다면 다음과 같다.

List<Fruit> appleList = extractFruitList(fruits, fruit-> "apple".equals(fruit.getName()));
List<Fruit> redList = extractFruitList(fruits, fruit-> "red".equals(fruit.getColor()));

https://skyoo2003.github.io/post/2016/11/09/java8-lambda-expression


반응형
반응형
지난번 글에서 RabbitMq의 기본개념에 대해 알아보았다. 추상적으로 밖에 이해가 안가서 다시 여러 글을 읽고 Spring Java를 이용하여 구현하여 보았다.

지난번 포스트에서 추가로 부연된 설명을 하고자 한다.

보통의 웹 어플리케이션이 직접 서버로 연결한 서버-클라이언트 관계라면
메세지 큐를 중간에 위치시킴으로서 라우터 역할을 수행한다.

메세지큐에게 메세지 전송을 모두위임하고 애플리케이션과 서버는 느슨한 관계를 유지할 수 있다.

느슨하게 결합된 시스템 구성의 장점은

1) 발행자 또는 소비자 간 문제 발생 시 서로 영향을 주지 않는다.
2) 각 시스템의 성능은 다른 측면에 영향을 미치지 않는다.
3) 발행자와 소비자의 인스턴스 수는 작업량을 수용할 수 있을 만큼 독립적으로 증가하고 감소한다.
4) 발행자는 소비자의, 소비자는 발행자의 위치가 어디인지 혹은 어떤 기술을 사용하는지 알지 못한다.





AMQP(Advanced Message Queuing Protocol) : 시스템 간 메시지를 교환하기 위해 공개 표준으로 정의한 프로토콜
Broker : 발행자가 만든 메시지를 저장
Virtual host : Broker 내의 가상 영역
Connection : 발생자와 소비자, Broker 사이의 물리적인 연결
Channel : 발행자와 소비자, Broker 사이의 논리적인 연결, 하나의 Connection 내에 다수의 Channel 설정 가능
Exchange : 발행한 모든 메시지가 처음 도달하는 지점으로 메시지가 목적지에 도달할 수 있도록 라우팅 규칙 적용, 라우팅 규칙에는 direct, topic, fanout
Queue : 메시지가 소비되기 전 대기하고 있는 최종 지점으로 Exchange 라우팅 규칙에 의해 단일 메시지가 복사되거나 다수의 큐에 도달할 수 있다
Binding : Exchange 와 Queue 간의 가상 연결

AMQP 모델에는 메세지 확인 개념이 있다. 메세지가 소비자에게 전달되면 Broker에서 알린다. 메세지 확인이 되면 브로커는 큐에서 메세지를 완전히 제거. 

중요한 개념이 Exchange이다. 발행자에게 수신한 메세지를 큐에 적절히 분배하는 라우터 기능을 한다. 각 Exchange와 큐는 가상으로 연결되어있고 이 바인딩 규칙에 따라 큐에 라우팅하는데 그것이 바로 Exchange 타입이다.


지난번 post에서는 조금 어렵게 설명해놓았는데 Exchange type을 다시 설명하려고 한다.

1) Direct Type - 라우팅키와 바인딩키가 완벽하게 일치해야 하는경우 -- 경우에 따라서 멀티 바인딩도 가능하다.




2) Fanout Type - 바운딩되어있는 모든 큐에 메세지를 전송한다. 바인딩, 라우팅키에 상관없이 



3) Topic Type - *와 # 사용 , *는 한개의 단어만 가능 #은 여러개의 단어가 가능
에를들어 rabbit.orange.queue라는 키는 Q1에만 가능 - * 자리에 한개의 단어만 사용하므로
또한 lazy.a.b.c...는 Q2에 가능 - 여러개의 단어가 연이어나와도 #이면 가능하다








즉, Topic type은 *가 오느냐 #가 오느냐에 따라  Fanout Type이 될수도 있고  Direct Type이 될수도 있다.









참조 블로그에 더 자세하게 나와있다.
https://brunch.co.kr/@springboot/6


반응형

'개발공부 > RabbitMq' 카테고리의 다른 글

RabbitMQ 기본 개념, AMQP의 정의  (0) 2018.06.08
반응형

"특정 시간에 특정 작업을 해야한다." 

 


1. 크론탭 기본 (crontab basic)
 crontab -e
-  콜론(:) 입력 후에 wq 를 입력해 크론탭을 갱신
 crontab -l 
- 표준 출력으로 크론탭 내용이 나온다.
 crontab -r
- 크론탭 삭제

2. 주기 결정

 

*      *      *      *      

(0-59)  시간(0-23)  일(1-31)  월(1-12)   2요일(0-7)

 
순서대로 분-시간-일-월-요일 순입니다.

그리고 괄호 안의 숫자 범위 내로 별 대신 입력 할 수 있습니다.

 

3. 주기별 예제

 

1) 매분 실행

 

* * * * * /home/script/test.sh

매분 test.sh 실행 

 

2) 특정 시간 실행

 

45 5 * * 5 /home/script/test.sh 

매주 금요일 오전 5시 45분에 test.sh 를 실행 

 

3) 반복 실행

 


0,20,40 * * * * /home/script/test.sh

매일 매시간 0분, 20분, 40분에 test.sh 를 실행 

 

4) 범위 실행

 

0-30 1 * * * /home/script/test.sh

매일 1시 0분부터 30분까지 매분 tesh.sh 를 실행 

 

5) 간격 실행

 


*/10 * * * * /home/script/test.sh

매 10분마다 test.sh 를 실행 

 

6) 조금 복잡하게 실행

 

*/10 2,3,4 5-6 * * /home/script/test.sh

5일에서 6일까지 2시,3시,4시에 매 10분마다 test.sh 를 실행 

 

주기 입력 방법엔 * , - / 을 이용하는 방법이 있습니다. 위에서 보셨듯이 각각의 특수기호가 하는 기능이 다르고 조합을 어떻게 하느냐에 따라 입맛대로 주기를 설정 할 수 있습니다.

 

4. 크론 사용 팁

 

1) 한 줄에 하나의 명령만 씁시다.

 

잘못된 예)

* * * 5 5

/home/script/test.sh

 잘된 예)

* * * 5 5 /home/script/test.sh

 
2) 주석

 

#############################

# 이것은 주석입니다.                    #

############################# 

 

# 을 입력해서 그 뒤로 나오는 모든 문자를 주석 처리할 수 있습니다.






출처: http://javafactory.tistory.com/119 [FreeLife의 저장소]


반응형
반응형
     1.  SPRING AOP 의 특징 

1)  스프링은 Aspect의 적용 대상(target)이 되는 객체에 대한 Proxy를 만들어 제공.
2) 대상객체(Target)를 사용하는 코드는 대상객체(Target)를 Proxy를 통해서 간접적으로 접근
3) Proxy는 공통기능(Advice)을 실행한 뒤 대상객체(Target)의 실제 메서드를 호출하거나 대상객체(Target)의 실제 메소드가 호출된 뒤 공통기능(Advice)을 실행

AOP에 대해 자세하게 알기 전에 먼저 proxy에 대해 알아보자. 

* 프록시란?
프록시 서버는 기존의 핵심기능이 수행되는 순간 그 기능을 가로채어 앞뒤로 미리 설정된 프록시 기능을 수행해주는 역할을 합니다.

프록시 패턴의 핵심원리는 기존의 기능의 앞, 뒤로 프록시 기능을수행만 하면 된다는 것입니다. 그러므로 기존 기능만 알아서 반복동작한다면 프로그래머는 앞뒤로 덧붙일 기능만 작성하면 됩니다.

다이나믹 프록시는 이런 원리에 입각하여 기존의 메서드를 전부 재작성할 필요 없이 하나의 메서드에 덧붙일 새로운 프록시 기능만 완성시키면 자동으로 모든 메서드에 동일한 프록시 기능을 생성시켜줍니다


기능 10개를 프록시로 작성한다면 늘어난 기능만큼 프록시를 작성해야 한다. 이런걸 막기 위해 자바에서 나온 개념이 바로 다이나믹 프록시이다.


MethodInterceptor 인터페이스: 스프링의 어드바이스를 작성하기 위해 구현해야 할 (스프링에서 사용되는) 다이나믹 프록시 인터페이스입니다.
----> 우리가 MethodInterceptor를 구현하게 되면 반드시 invoke란 메서드를 구현해야 합니다. invoke메서드에 기존의 기능에 앞뒤로 덧붙이기만 하면 다이나믹 프록시가 완성됩니다. 그리고 이것을 포인트컷과 함께 어드바이저에 넘겨주면 끝 (용어를 모르겠다면 밑을 참고!)

     3.  용어 정리

1) 어드바이스(Advice): 특정 조인포인트에서 관점이 취하는 행동(action). "around", "before", "after" 어드바이스같은 여러 가지 타입의 어드바이스가 있다.  -  Joinpoint에서 실행되어야 하는 코드

2) 조인 포인트(Join point): 메서드의 실행이나 예외 처리같은 프로그램이 실행되는 중의 어떤 지점이다. 스프링 AOP에서 조인포인트는 항상 메서드 실행을 나타낸다. - 특정 작업이 실행되는 '시점'을 의미한다.

3) 관점(Aspect): 여러 클레스에 걸친 관심사의 모듈 - Advice와 Pointcut을 합쳐서 하나의 Aspect라고 한다.

4) 포인트컷(Pointcut): 조인포인트를 매칭하는 것(predicate)이다. 어드바이스는 포인트컷 표현식과 연결되고 포인트컷이 매치한 조인포인트에서 실행된다. - Target 클래스와 Advice가 결합(Weaving)될 때 둘 사이의 결합규칙을 정의하는 것이다

5) Weaving : AOP에서 Joinpoint들을 Advice로 감싸는 과정을 Weaving이라고 한다. Weaving 하는 작업을 도와주는 것이 AOP 툴이 하는 역할이다.

6) Target : 실질적인 비지니스 로직을 구현하고 있는 코드

어드바이스(Advice)란 특정 조인포인트에서 관점이 취하는 행동이라고 했다. 그렇다면 advice의 다양한 타입을 알아보자.

1) Before advice: joinpoint전에 수행되는 advice. 하지만 joinpoint를 위한 수행 흐름 처리(execution flow proceeding)를 막기위한 능력은 없다.
2) After returning advice: 메소드가 예외를 던지는것 없이 반환된다면 joinpoint 가 완성된 후에 수행되는 advice.
3) After throwing advice: 메소드가 예외를 던져서 빠져나갈때 수행되는 advice
4) After (finally) advice: join point를 빠져나가는 방법에 상관없이 수행되는 advice.
5) Around advice: 이것은 가장 강력한 종류의 advice이다. Around advice는 메소드 호출 전후에 사용자 정의 행위를 수행할수 있다. 짧게 수행하거나 예외를 던지는 것인지에 대해 책임을 진다.


    4. 포인트컷과 어드바이스

사실 설명만으로는 감이 잘 안올것이다. Spring에서 Aop 기술을 활용한 트랜잭션 예제를 살펴보며 이해하자.
저번 스터디때 트랜잭션은 두가지 방법이 있다고 했다. 그 중, 선언적 방법에는 @Transactional 어노테이션을 활용하는 방법과 <tx:advice>를 이용하는 방법이 있다. 우리 프로젝트(toyproject)에는 어노테이션 방법을 쓸 것이지만 <tx:advice>에 대해 개념은 알아두어야 할 것이다. 지금부터 알아보자!

<tx:annotation-driven />
<aop:config>
     <aop:advisor advice-ref="transactionAdvice" pointcut="bean(*Service)" />
</aop:config>

<tx:advice id="transactionAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="exceptionPut" propagation="REQUIRES_NEW" />
<tx:method name="get*" propagation="REQUIRED" read-only="true" />
<tx:method name="*" />
</tx:attributes>
</tx:advice>

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>


이 소스에 관해 하나씩 분석 해보자. 먼저 pointcut 에 관한 설명을 해보겠다.

<aop:advisor pointcut="bean(*Service)" advice-ref="transactionAdvice" />

"bean(*Service)"처럼 설정한다면 빈 중에서 Service로 끝나는 모든 빈을 선택하게 됩니다.

포인트컷은 학습테스트나 특별한 기능을 위해서라면 org.springframework.aop.Pointcut 인터페이스를 구현하여 따로 클래스로 만들 수도 있지만 보통은 위의 예제처럼 간편하게 <aop:advisor> 내에 위치시키는 방법이 일반적입니다. 다만 하나 이상의 포인트컷을 입력하고 싶다면 아래 이미지와 같이 구성할 수도 있습니다.



다음으로 우리가 알아볼 것은 바로 어드바이스입니다.


<tx:advice id="transactionAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="exceptionPut" propagation="REQUIRES_NEW" />
<tx:method name="get*" propagation="REQUIRED" read-only="true" />
<tx:method name="*" />
</tx:attributes>
</tx:advice>

어드바이스는 포인트컷으로 선택한 빈에 적용될 부가기능을 뜻합니다. AOP는 트랜잭션만을 위한 기술이 아니기 때문에 필요하다면 어드바이스를 통해 포인트컷으로 선별될 빈(또는 클래스, 메서드)의 적용 기술을 자유롭게 변경할 수 있습니다. 물론 여기서 우리가 적용할 부가기능은 바로 트랜잭션입니다.

이런 어드바이스는 org.aopalliance.intercept.MethodInterceptor를 구현하여 만들어집니다. ( MethodInterceptor 인터페이스: 스프링의 어드바이스를 작성하기 위해 구현해야 할 (스프링에서 사용되는) 다이나믹 프록시 인터페이스라고 위에 설명해놓았다. 2번 참고.)

다음은 어드바이저이다.
포인트컷과 어드바이스를 통해 어드바이저를 만들고, 어드바이저는 어드바이스와 포인트컷의 정보를 참조하는 기능을 한다. 다음과 같은 그림을 보면 좀더 이해하기 편하겠다. 앞의  통 소스와 그림과 함께 보면 더욱 이해가 빠를 것이다.
5. 자동프록시생성기

<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator" />

자동프록시생성기는 기본적으로 org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator 클래스를 뜻합니다. 이 클래스가 빈으로 등록되어 있다면 자동으로 Advisor 인터페이스를 구현한 빈이 컨텍스트에 등록되었는지를 확인해주고 해당 어드바이저들에 정의되있는 포인트컷과 어드바이스를 해석하여 해당 요청이 왔을 때 자동으로 이 요청을 가로채어 프록시를 생성해줍니다. 사용법은 컨텍스트에 해당 빈을 생성해주기만 하는 것으로 끝이 납니다.

단, AOP 기술을 사용한다면  <aop:~>태그만 사용한다면 등록된 어드바이저들은 모두 자동프록시생성기를 통해 해당 포인트컷으로 클래스를 인터셉터하고 어드바이스에 등록된 기능을 수행한다는 보장한다. 


출처: http://truepia.tistory.com/275 [진실세상을 꿈꾸며]

http://egloos.zum.com/springmvc/v/498979

출처: http://devjms.tistory.com/70 [얼렁뚱땅 개발자]

출처  http://wiki.gurubee.net/pages/viewpage.action?pageId=26740833



반응형

'개발공부 > Spring' 카테고리의 다른 글

예외처리(1)  (0) 2020.10.01
Spring에서 Bean을 주입하는 방식  (0) 2020.03.16
SLF4J와 Logback  (1) 2018.06.26
Tiles란, Spring tiles3 적용법  (2) 2018.06.14
반응형

JDK 8 API 에서 파일에 대해 함수형 스트림을 사용할 수 있는 메서드가 추가되었다. 
- find를 이용해서 어떤 디렉토리에 존재하는 모든 파일을 가져오도록 하는 함수


Path start = Paths.get("");
int maxDepth = 5; // Integer.MAX_VALUE로 하면 모든 파일을 가져올 수 있다
try (Stream<Path> stream = Files.find(start, maxDepth, (path, attr) ->
        String.valueOf(path).endsWith(".js"))) {
    String joined = stream
        .sorted()
        .map(String::valueOf)
        .collect(Collectors.joining("; "));
    System.out.println("Found: " + joined);
}


 위 예제에선 현재 작업 디렉터리 내의 모든 파일을 훑는다. 그리고 각각의 경로를 각자의 문자 표현식(valueOf 메서드)으로 맵핑한다. 그 결과는 필터되고, 정렬되고 마지막으로 합쳐진(joined) 하나의 문자열이다. 만약 함수형 스트림에 익숙하지 않다면 Java 8 Stream Tutorial 을 읽어보길 권한다.

  find 메서드는 인자 3개를 받는다. 첫째는 start는 시작 경로이며 둘째는 maxDepth로 말 그대로 탐색할 최대 폴더 깊이를 의미한다. 마지막 세 번째는 탐색 로직을 결정하는 람다식이 들어가며 타입은 predicate이다. 위 예에서 모든 .js로 끝나는 javascript파일을 찾았다.

try/with 문장으로 감싸져 있다는 것을 깨달았을 것이다. Stream들은 AutoCloseable를 구현하고 있으며 이 경우 반드시 명시적으로 스트림을 닫아 주어야만 한다. 이는 뒷쪽에서 IO 연산이 수행되기 때문이다.


http://dextto.tistory.com/215



반응형

'개발공부 > JAVA8' 카테고리의 다른 글

JAVA8 - 람다  (0) 2018.07.18
반응형
  1.  SLF4J 로깅 처리

  2. 세상에는 여러 Logger들이 존재한다. 무엇을 쓸지는 크게 상관없지만 어플리케이션을 개발할때는 한가지 선택 해야하고, 중요한 것은 의존된 라이브러리가 쓰고 있는 Logger도 잘 확인해야 함. 섞어 쓸 수도 있지만, logging을 제어하려면 하나로 통일 되야 좋다.  여기서 내가 쓰고 싶은 Logger로 통일하고 싶다면 방법은 SLF4J이다.

    SLF4J는 로깅 Facade입니다. 로깅에 대한 추상 레이어를 제공하는것이고 java로 따지면 interface덩어리 라고 보시면 됩니다. artifact이름도 api라고 부릅니다.

    • 사용방법 - pom.xml에 다음과 같은 의존을 추가한다

    <dependency>
      <groupId>org.slf4j</groupId>
       <artifactId>jcl-over-slf4j</artifactId>
      <version>1.7.7</version>
    </dependency>


    실행 코드는 다음과 같다.

    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    public class Sample {
      final Logger logger = LoggerFactory.getLogger(Sample.class);
      public void run() {
      logger.debug("debug");
      logger.info("info");
      }
    }


    하지만 이를 실행하면 다음과 같은 오류가 난다.

    SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".SLF4J: Defaulting to no-operation (NOP) logger implementationSLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.


    구현체가 없기 때문이다. 여기서 구현체란  commons logging, log4j, java util logging, logback 이런 종류의 로깅 구현체를 말한다. 



    1. Logback

    여러 구현체가 존재하지만 나는 Logback을 공부해왔고, 우리 프로젝트에 적용하고 싶다. 그 이유는 다음과 같다. 

    - system.out.println 한줄 남기는데 io 리소스를 너무 많이 먹어서 시스템이 느려짐
    - log4j는 메모리 관리를 따로해서 io쪽에서 메모리를 많이 안먹고 빠르다.
    - log4j를 막 쓰다 보니.. system.out.println 만큼 리소스를 먹게 된다..
    - 이를 보안하기 위해 logback이 개발 됨


    즉, “log4j“에서 제공을 하지 않는 기능 외에 다양한 이점이 있음.

    다양한 이점을 더 알고 싶다면 다음을 참고하자!


    1) logback의 3가지 컴포넌트

    “logback-core“는 말 그대로 핵심 코어 컴포넌트 입니다. “logback-classic“은 “slf4j“에서 사용이 가능하도록 만든 플러그인 컴포넌트 입니다.
    “logback-access“는 사용하는 어플리케이션이 “웹 어플리케이션“일 경우 빛을 바라는 컴포넌트 입니다.” HTTP 요청에 대한 강력한 디버깅 기능을 제공 합니다.”

    2) Graceful recovery from I/O failures

    “Log4j” 같은 경우 “JDBC” , “Socket“등 다양한 “Appender“를 지원 합니다.특히. 대다수의 환경에서는 “File”에 제일 많이 로그를 저장 할 것입니다.
    하지만 만약 “파일 서버가 일시적으로 장애가 발생 할경우” , “파일 서버가 복구 될때까지”“어플리케이션을 중지 시켰다가” 다시 파일 서버가 복구되면, 그때 서버를 재기동 할것 입니다. 하지만 “LOGBack“은 “서버 중지 없이, 이전 시점부터 복구를 graceful “하게 지원을 합니다.

    3) Automatic removal of old log archives

    대부분 환경에서는 “하나의 파일”에 기록하는 것이 아니고, “특정 시간” 또는 “특정 파일 사이즈”로 “Rolling” 해서 “Archiving“을 할 것입니다.
    하지만 “Archiving된 로그 파일“을 계속 유지하지 않고, 일정 기간 지나면 서비스에 부담을 주지 않기 위해서” 파일을 삭제 할것 입니다.
    이럴경우 “Crontab” 또는 다른 삭제 스케줄러를 설정 또는 개발을 할것입니다.“LOGBack“은 “maxHistory“라는 설정 정보를 통해서 주기적으로 “Archive”
    파일을 자동 삭제 합니다.“maxHistory“의 값이 “12“일 경우, “12개월 이후 삭제 하라는” 뜻입니다.

    4) Lilith
    “Lilith“은 “현재 발생하는 로그 이벤트에 대한 상태 정보를 볼수 있도록 지원 하는
    뷰어” 입니다.“log4j“를 지원하는 “chainsaw” 와 비슷한 기능을 합니다. 다만 차이점은 “Lilith” “Large data“를 지원 합니다.

    5) Conditional processing of configuration files

    빌드를 해본 사람이라면 아실듯 합니다. 빌드시 제일 골치가 아픈 것이 “config” 정보 와 “log file path” 입니다.
    이유는 어플리케이션이 구동하는 환경이 “로컬, staging, cbt” 마다 틀리기 때문 입니다. 이런점을 해결 하기 위해서 “Maven Filter” 기능을 사용 하거나, “JVM -D환경변수“를 통해서 각 환경 마다 선택적으로 “log 설정파일“을 선택하도록 구성을 할 것입니다.
    “LOGBack“은 이런 이슈를 좀더 “Graceful“하게 지원을 합니다. “JSTL” 같이 분기 스크립트 (<if>, <then> and <else>)를 제공해서 하나의 파일에서 다양한 빌드 환경을 제공 하도록 지원을 합니다.

    6) Filters

    “Filter” 기능은 “Log4j“에서 지원하는 기능입니다. 주 기능은 “로깅을 남길지 , 말지를”핸드링 할수 있는 기능 입니다.
    좀더 이해를 돕기 위해서 “UseCase“를 설명 드리겠습니다.

    만약 “A“라는 사용자에게 비즈니스적으로 문제점이 발견이 되었습니다.이럴경우는 “logical exception“이기 때문에 원인을 잡기가 쉽지가 않습니다.
    또한 현재 “운영 서버“의 “Log LEVEL“이 “ERROR“일 경우 로그를 확인 할수가 없을것입니다. 더욱이  “운영 서버 logical exception“은 “staging” 환경과 데이터가 틀리기 때문에실제 운영 로그를 확인하지 않고서는 재현을 할수가 없습니다.

    그래서 “운영 서버의 로그 레벨“을 “DEBUG“로 수정합니다. 하지만 로그는 보이지만 전체 사용자가해당이 되기 때문에 로그 데이터를 분석하기가 어렵습니다.
    이럴 경우 “Filter“를 사용해서 “다른 사용자“는 “ERROR” 레벨을 유지하고 ,"A” 사용자만 “DEBUG“로 설정을 하면, 분석하는데 많은 도움을 받을 수 있습니다.

    7) SiftingAppender

    “SiftingAppender“는 “Filter“의 기능과 유사하면서 다른 기능을 제공 합니다 “로그 파일을 특정 주제별로 분류“를 할 수 있도록 합니다.
    예를 들어서 “HTTP Session“별로 로그 파일을 저장한다거나, “사용자별“로 별도의 로그파일을 할 수 있습니다.

    8) Logback-access, i.e. HTTP-access logging with brains, is an integral part of logback

    위에서 언급했듯이 “logback-access“는 “웹 어플리케이션” 사용시 유용한 툴로써 제공 됩니다.특히나 요새는 전통적인 “HTML” 출력이 아닌 “REST 서버“로써의 역할을 많이 합니다. 이럴 경우 “HTTP 디버깅“을 제공 합니다.


    3. Logback 실습 

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>3.2.4.RELEASE</version>
    </dependency>

    하지만 spring-context는 jarkarta commons logging을 사용하고 있습니다. 실제 이렇게 두면 commons-logging이 의존성에 추가 됩니다. 여기서는 logback을 쓰려고 하니 관련라이브러리는 제거합니다.

    ** 제거하는 방법

         1) pom.xml




    2) Dependency Hierarchy -> commons-logging 우클릭 -> exclude maven artifact


    3) ok


    4) pom.xml에 추가 됬는지 확인





    5)  logback 은 logback-core, logback-classic, logback-access의 3개의 모듈이 있습니다. core는 classic과 access의 공통라이브러리입니다. maven repository를 쓴다면 classic만 추가하면 관련 라이브러리가 추가 됩니다.

    <dependencies>
      <dependency>
      <groupId>ch.qos.logback</groupId>
      <artifactId>logback-classic</artifactId>
      <version>1.1.2</version>
      </dependency>
    </dependencies>

    이것만 사용하면 기본적인 Log는 사용 가능하다. 

    import ch.qos.logback.classic.LoggerContext;
    import ch.qos.logback.core.util.StatusPrinter;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;

    public class Tutorial {
      private static final Logger logger = LoggerFactory.getLogger(Tutorial.class); 
     // Logger은 꼭 private static이 붙어야 한다. getLogger(클래스명.clss) 적어라.

      public static void main(String[] args) {
      logger.trace("trace");
      logger.debug("debug");
      logger.info("info");
      logger.warn("warn");
      logger.error("error");
      }
    }

    ** Logger들은 Tree Hierarchy 구조로 level을 적용 받을수 있다.




    TRACE > DEBUG > INFO > WARN

    ALL : TRACE와 같음 나중에 확장을 위한것으로 보임.
    TRACE : TRACE, DEBUG, INFO, WARN, ERROR
    DEBUG : DEBUG, INFO, WARN, ERROR
    INFO : INFO, WARN, ERROR
    WARN : WARN, ERROR
    ERROR : ERROR
    OFF : 출력하지 않음

    여기까지 표준이다. 하지만, 좀 더 기능을 확장하고 싶다면 logback.xml을 작성해야 한다.


    Logback.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <configuration> //logback이 구동될때 logback 상태를 확인할 수 있습니다.
      <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
      <layout class="ch.qos.logback.classic.PatternLayout">
      <Pattern>%d{HH:mm} %-5level %logger{36} - %msg%n</Pattern>
      </layout>
      </appender>

      <logger name="example.logback.level.grandparents" level="TRACE"/>
    //example.logback.level.grandparents 이하 모든 Logger들의 level은 TRACE라는 설정입니다.

      <logger name="example.logback.level.grandparents.parents.children" level="INFO"/>
    //example.logback.level.grandparents.parents.children 이하 모든 Logger들의 level은 INFO라는 설정입니다.

      <root level="DEBUG">
                 <appender-ref ref="STDOUT" />
      </root>
    // 모든 대상에 STDOUT Appender를 적용하고 level이 DEBUG이하인것만 처리로 설정됩니다.
    </configuration>



    1. Appender
    Event마다 Log를 기록하는 기능은 Appender가 처리합니다. 그래서 Logger는 어떤 appender에 해당이 되어 처리 되는게 중요합니다. Appender를 설정하더래도 log출력에 해당되지 않으면 작동하지 않습니다.  Appender는 출력될 형식을 직접 가지고 있지 않고, 해당 기능은 Layout과 Encoder에 위임을 합니다.


    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
      <!-- encoders are assigned the type
      ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
      <encoder>
      <pattern>%-4relative [%thread] %-5level %logger{35} - %msg %n</pattern>
      </encoder>
    </appender>

    Appender의 종류 - ConsoleAppender (콘솔로 찍음), FileAppender(파일에 로그남김), RollingFileAppender(rolling 정책이용) ,  EventEvaluator(이벤트 기능으로 로그 남김)

    1) ConsoleAppender 

    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
      <!-- encoders are assigned the type
      ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
      <encoder>
      <pattern>%-4relative [%thread] %-5level %logger{35} - %msg %n</pattern>
      </encoder>
    </appender>

    PatternLayoutEncoder는 pattern으로 받은 값을 이용해서 PatternLayout을 생성하고 PatternLayoutEncoder는 log message를 byte[]로 변환하는 기능도 포함합니다. 이로써 Appender는 Layout기능과 Encoder기능을 모두 가지게 됩니다. 이것을 이용해서 OutputStreamAppender는 byte[]를 OuputStream에 write하게 됩니다.

    2) FileAppender

    <!-- Insert the current time formatted as "yyyyMMdd'T'HHmmss" under
      the key "bySecond" into the logger context. This value will be
      available to all subsequent configuration elements. -->
    <timestamp key="bySecond" datePattern="yyyyMMdd'T'HHmmss"/>
    <appender name="FILE" class="ch.qos.logback.core.FileAppender">
           <file>log-${bySecond}.txt</file>
           <append>true</append>
      <!-- encoders are assigned the type
      ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
      <encoder>
      <pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>
      </encoder>
    </appender>

    로그가 저장될 file을 선언하고, ConsoleAppender처럼 encoder,pattern을 선언하게 되면, log event를 지정된 file에 저장할 수 있습니다. 이때 파일 포맷중 날짜형식은 java.text.SimpleDateFormat을 따릅니다.

    3) RollingFileAppender
    롤링 정책 적용. 최대 파일 용량 설정가능

    <appender name="ROLLING" class="ch.qos.logback.core.rolling.RollingFileAppender">
      <file>mylog.txt</file>
      <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
      <!-- rollover daily -->
      <fileNamePattern>mylog-%d{yyyy-MM-dd}.%i.txt</fileNamePattern>
      <timeBasedFileNamingAndTriggeringPolicy
      class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
      <!-- or whenever the file size reaches 100MB -->
      <maxFileSize>100MB</maxFileSize>
      </timeBasedFileNamingAndTriggeringPolicy>
      </rollingPolicy>
      <encoder>
      <pattern>%msg%n</pattern>
      </encoder>
    </appender>

    <fileNamePattern> 확장자에 .zip을 선언하면 새로운 file 이 생성될때 이전 파일은 .zip으로 압축을 할수 있습니다.

    4) EventEvaluator
    이벤트 설정. 아래 예는 이메일 설정한 예이다. 이메일 로그 설정 관련 자세한 내용을 보고 싶다면  https://sonegy.wordpress.com/2014/06/03/235/ 출처 참고!

    <appender name="EMAIL" class="ch.qos.logback.classic.net.SMTPAppender">
      <smtpHost>****</smtpHost>
      <smtpPort>25</smtpPort>
      <to>****</to>
      <to>****</to>
      <from>****</from>
      <subject>PRODUCT-PCS: %logger{20} - %m</subject>
      <layout class="ch.qos.logback.classic.PatternLayout">
      <Pattern>%d{HH:mm} %-5level %logger{36} - %msg%n</Pattern>
      </layout>
    </appender>


    ** 공통으로 적용되었던 Layout 패턴에 대해 알아보자!


    • %logger{length}: Logger name의 이름을 축약할 수 있습니다. {length}는 최대 차릿수 입니다.
    • %thread: 현재 Thread name
    • %-5level: log level -5는 출력 고정폭 값
    • %msg: log message %message은 alias
    • %n: new line

    로그 적용시 에러관련 

    출처: http://madeinjeon.tistory.com/18 [Jeon's blog]



반응형

'개발공부 > Spring' 카테고리의 다른 글

예외처리(1)  (0) 2020.10.01
Spring에서 Bean을 주입하는 방식  (0) 2020.03.16
SPRING AOP  (0) 2018.07.02
Tiles란, Spring tiles3 적용법  (2) 2018.06.14
반응형
<Tiles 기본>
  1.  tiles 란?
-  반복적으로 사용되는 header, footer와 같은 정보를 한곳에 모아둔 프레임 워크

  1.   tiles3로 오면서 설정이 더욱 간단해 졌다. tiles3는 spring3.2 버전 이상부터 쓰일 수 있으니 참고바람.

  1.     pom.xml에 3가지 dependency를 추가한다.

pom.xml

<dependency>
    <groupId>org.apache.tiles</groupId>
    <artifactId>tiles-extras</artifactId>
    <version>3.0.0</version>
</dependency>
<dependency>
    <groupId>org.apache.tiles</groupId>
    <artifactId>tiles-jsp</artifactId>
    <version>3.0.0</version>
</dependency>
<dependency>
    <groupId>org.apache.tiles</groupId>
    <artifactId>tiles-servlet</artifactId>
    <version>3.0.0</version>
</dependency></p>


  1.    tiles를 유동적으로 사용하고 싶다면 다음과 같이 설정한다.  우선 모든 view들은 tiles를 정의한 tiles 설정 파일의  definition들에서
binding될 jsp 등의 view 파일들을 찾게됩니다. 만약 각 views.xml에서 view 파일을 못찾게 되면 우선순위가 2인 InternalResourceViewResolver 에서 해당 view 파일을 찾게 됩니다.

참고 >> 
view resolver
- View Resolver는 Spring의 DispatcherServlet이 Controller의 응답 결과를 가져올 때, 어떤 방식으로 view 객체를 가져올지 정하고 해당 객체를 생성한다.
- View Resolver를 구현한 클래스는 InternalResourceViewResolver, BeanNameViewResolver, XmlViewResolver, UrlBasedViewResolver 등이 있다.
- DispatcherServlet은 여러개의 ViewResolver를 가질 수 있는데, 'order' property에 따라 어떤 ViewResolver를 결정할지 우선순위를 정할 수 있다. 우선순위를 명시하지 않으면 가장 낮은 우선순위를 가진다.
- 우선순위가 높은 ViewResolver에게 View 객체를 요청하는데, 만약 null을 리턴한다면 그 다음 우선순위의 ViewResolver에게 객체를 요청하게 된다.
- InternalResourceViewResolver는 항상 mapping되는 view 객체를 리턴하고, 없다고 null을 반환하지 않으므로 항상 가장 낮은 우선순위로 두어야한다.



dispatcher-servlet.xml

<bean id="tilesViewResolver" class="org.springframework.web.servlet.view.UrlBasedViewResolver">
  <property name="viewClass" value="org.springframework.web.servlet.view.tiles3.TilesView" />
  <property name="order" value="1" />
</bean>

// tiles의 우선순위를 1로 설정해둔다.

 <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
  <property name="prefix" value="/WEB-INF/views/"/>
  <property name="suffix" value=".jsp"/>
  <property name="order" value="2"></property>
 </bean>

//기존에 써있던 view resolver 에서 우선순위만 바꿔줬다.


<bean id="tilesConfigurer" class="org.springframework.web.servlet.view.tiles3.TilesConfigurer">
  <property name="definitions">
  <list>
  <value>/WEB-INF/tiles/tiles.xml</value>
  </list>
  </property>
</bean>

//tiles관련 설정파일 
//만약 여러개 하고 싶다면 list의 value를 추가해주면 된다.


  1. 다음은 tiles 설정파일 tills.xml

<!DOCTYPE tiles-definitions PUBLIC "-//Apache Software Foundation//DTD Tiles Configuration 3.0//EN" "http://tiles.apache.org/dtds/tiles-config_3_0.dtd">

<tiles-definitions>
  <definition name="default" template="/WEB-INF/views/layout/layout.jsp">
  <put-attribute name="header" value="/WEB-INF/views/layout/header.jsp" />
  <put-attribute name="body" value=""/>
  <put-attribute name="footer" value="/WEB-INF/views/layout/footer.jsp" />
  </definition>

//기본적인 defiinition 태그를 하나 만들어둔다.

  <definition name="tiles" extends="default">
<put-attribute name="body" value="/WEB-INF/views/layout/mainTest.jsp" />
</definition>

//  defiinition 을 상속받아 여러 타일즈를 만들수 있다.

</tiles-definitions>


빨간색으로 표시한 definition name="tiles" <- 이부분이 중요하다.  이부분때문에 오류가 많이난다.

  1. layout.jsp

<%@ page language="java" contentType="text/html; charset=EUC-KR"
  pageEncoding="EUC-KR"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="tiles" uri="http://tiles.apache.org/tags-tiles"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=EUC-KR">
</head>

<body>
  <div id="wrap">
  <div >
  <div>
  <div>
  <tiles:insertAttribute name="header" />
  </div>
  </div>
  <div>
  <div>
  <tiles:insertAttribute name="body" />
  </div>
  </div>
  <div>
  <div>
  <tiles:insertAttribute name="footer" />
  </div>
  </div>
  </div>
  </div>
</body>
</html>



기본적으로 만들고 싶은 틀을 html 형식으로 만들어 준다.    <tiles:insertAttribute name="footer" /> 의 name은 tiles.xml에서 설정한 name과 같아야 한다. 

  1. Controller mapping 추가

@RequestMapping(value = "/test", method = RequestMethod.GET)
  public String hello(Locale locale, Model model) {

  Date date = new Date();
  DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.LONG, DateFormat.LONG, locale);

  String formattedDate = dateFormat.format(date);

  model.addAttribute("serverTime", formattedDate );
  return "tiles";
  }


return 값과 definition에서 정의한 name이 반드시 일치하게 해야 404 에러가 안난다. 기본적인 설정은 완료하였다. 
응용버전은 추후 추가하도록 하겠다.

  1. 기본적인 부분 완성!  - header.jsp , footer.jsp, mainTest.jsp 는 커밋해놓았으니 각자 코드를 확인해보기 바란다!



<Tiles 응용>
앞서 소개한 tiles 를 우리 프로젝트에 응용할 수 있도록 해보겠다.





우리가 앞서 소개한 부분은 defulat이다. 근데 나는 left 메뉴가 있는화면도 필요하다면 tiles.xml에 <definition>을 추가할 수 있다.


<!DOCTYPE tiles-definitions PUBLIC "-//Apache Software Foundation//DTD Tiles Configuration 3.0//EN" "http://tiles.apache.org/dtds/tiles-config_3_0.dtd">

<tiles-definitions>
  <definition name="default" template="/WEB-INF/views/layout/layout.jsp">
<put-attribute name="header" value="/WEB-INF/views/layout/header.jsp" />
<put-attribute name="body" value=""/>
<put-attribute name="footer" value="/WEB-INF/views/layout/footer.jsp" />
  </definition>

<1>
  <definition name="withLeft" template="/WEB-INF/views/layout/layout_left.jsp">
<put-attribute name="header" value="/WEB-INF/views/layout/header.jsp" />
<put-attribute name="left" value=""/>
<put-attribute name="body" value=""/>
<put-attribute name="footer" value="/WEB-INF/views/layout/footer.jsp" />
  </definition>  

  <definition name="tiles" extends="default">
<put-attribute name="body" value="/WEB-INF/views/layout/mainTest.jsp" />
</definition>

<2>
<definition name="user/*/*" extends="withLeft">
<put-attribute name="left" value="/WEB-INF/views/layout/left.jsp"/>
<put-attribute name="body" value="/WEB-INF/views/{1}/{2}.jsp" />
</definition>



</tiles-definitions>


<1>  template을 다르게 해서  definition을 추가했다. 이를 상속받아 <2>를 추가했다.

여기서 중요한것은 <2>번이다. 다들 이를 응용하여 개발하면 된다.
definition name="/" -- > controller mapping 형태를 말한다. 

참고>>>
definition name 형식 -
- ? : 한 글자, * : 아무글자나, [abc] : abc로 시작

IssueController.java
@RequestMapping(value={"/issue/list"} , method=RequestMethod.GET)
public String showIssueList(HttpServletRequest request) {
return "issue/issueList";
}
}


/issue/list를 매핑하여  issue/issueList를 return 한다.
그러면  issue가 {1}  issueList가 {2}가 된다.
즉 issue 폴더 아래에 있는 issueList에 적용한다는 것이다.


http://localhost:8081/toyProject/issue/list  를 호출하면 다음과 같은 화면이 나온다. 




layout_left.jsp 가 적용된 화면이니 이를 응용하여 프로젝트에 활용하면 된다 . 끝!







출처: http://creator1022.tistory.com/176 [무한도전]







반응형

'개발공부 > Spring' 카테고리의 다른 글

예외처리(1)  (0) 2020.10.01
Spring에서 Bean을 주입하는 방식  (0) 2020.03.16
SPRING AOP  (0) 2018.07.02
SLF4J와 Logback  (1) 2018.06.26
반응형
zookeeper(주키퍼)란?
분산 처리 시스템에서 일괄적으로 관리해주는 시스템

분산처리 환경에서는 기본적으로 서버가 몇대에서 수백대까지도 구축 가능 
- 이런 환경에서는 예상치 못하는 예외적인 부분이 많이 발생 (네트워크장애, 일부 서비스 기능 중지, 서비스 업그레이드 및 확장)

즉, 싱글 서버에서는 문제가 되지 않으나 멀티 서버 관리를 하면 문제가 될 수도 있는ㄷ 이러한 문제점들을 쉽게 해결해준다.

1)  네임서비스를 통한 부하분산
- 하나의 서버에만 서비스가 집중되지 않게 서비스를 알맞게 분산해 각각의 클라이언트들이 동시에 작업하게 해준다
- DNS 서버에 어느 서버로접속해야 하는지 물어보고 나서 직접 서버에 연결하는 방식

2) 데이터의 안정성 보장
- 하나의 서버에서 처리한 결과를 다른 서버들과 동기화하여 데이터의 안정성을 보장한다.

3) 장애상황 판단 및 복구
- 운영서버에 문제가 발생해서 서비스를 제공할 수 없을 경우, 다른 대기중인 서버를 운영 서버로 바꿔서 서비스가 중지없이 제공되게 한다.

4) 환경설정 관리
분산환경을 구성하는 서버의 환경설정을 따로 분산하지 않고 주키퍼 자체적으로 관리한다.

주키퍼는 다중의 서버 집합을 묶어서 관리해주는 시스템인데 그 중에서도 리더가 되는 서버 하나가 존재한다.
모든 서버의 중심이 되는 서버, 또한 하나의 서버에서 처리가 되어 데이터가 변경되면 모든 서버에 전달되어 동기화를 하게 된다.

분산 환경에서의 주키퍼 서버는 일반적으로 세대 이상을 사용하고 홀수로 구성한다. - 과반수룰을 적용하기 위해

SPOF(single Point Of Failure) 처리

액티브 서버 : 현재 서비스를 하고있는 서버

스탠바이 서버 : 장애 발생시 대처하기 위한 대기 서버

동작중이던 액티브 서버가 장애가 발생 할 경우 관리시스템이 판단하여 기존의 스탠바이 서버를 액티브 서버로 전환하여 서비스를 처리


주키퍼의 기본 구조와 상세 활용법에 대해서는 추후 업로드 하도록 하겠다.




출처: http://creatorw.tistory.com/entry/2-주키퍼zookeeper-기본-구조 []

출처: http://creatorw.tistory.com/entry/2-주키퍼zookeeper-기본-구조 []
출처 : http://exem-academy.com/?p=2927


반응형

+ Recent posts