레시피 2-2내용은 별 내용 없어서 생략했다.


 이번 장에서는 POJO 인스턴스에 대한 레퍼런스 자동 연결 기능을 보도록 하자.



1. Autowired


 POJO 필드에 @Autowired 어노테이션을 붙이면 그 필드에 호환되는 빈을 찾아서 연결한다.

 필드 변수를 초기화해주지 않아도 자동으로 호환되는 레퍼런스를 찾아 초기화해준다는 것이다.


 다음 예제는 이전 포스팅에서 다루었던 SequenceGenerator 예제를 변형한 것이다.


Sequence

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
package com.apress.springrecipes.sequence;
 
public class Sequence {
    private final String id;
    private final String prefix;
    private final String suffix;
    
    public Sequence(String id, String prefix, String suffix) {
        this.id = id;
        this.prefix = prefix;
        this.suffix = suffix;
    }
    
    public String getId() {
        return id;
    }
    
    public String getPrefix() {
        return prefix;
    }
    
    public String getSuffix() {
        return suffix;
    }
    
    
}
 
cs


SequenceDao

1
2
3
4
5
6
7
8
package com.apress.springrecipes.sequence;
 
public interface SequenceDao {
    public Sequence getSequence(String sequenceId);
    
    public int getNextValue(String sequenceId);
}
 
cs


SequenceDaoImpl

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
package com.apress.springrecipes.sequence;
 
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
 
import org.springframework.context.annotation.ComponentScan;
import org.springframework.stereotype.Component;
 
@Component("sequenceDao")
public class SequenceDaoImpl implements SequenceDao {
    private final Map<String, Sequence> sequences = new HashMap<>();
    private final Map<String, AtomicInteger> values = new HashMap<>();
    
    public SequenceDaoImpl() {
        sequences.put("IT"new Sequence("IT""30""A"));
        values.put("IT"new AtomicInteger(10000));
    }
    
    public Sequence getSequence(String sequenceId) {
        return sequences.get(sequenceId);
    }
    
    public int getNextValue(String sequenceId) {
        AtomicInteger value = values.get(sequenceId);
        return value.getAndIncrement();
    }
}
 
cs


Main

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.apress.springrecipes.sequence;
 
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
 
public class Main {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext("com.apress.springrecipes.sequence");
        
        SequenceService sequenceService = context.getBean(SequenceService.class);
        
        System.out.println(sequenceService.generate("IT"));
        System.out.println(sequenceService.generate("IT"));
    }
}
 
cs


여기까지는 이전 포스팅과 똑같다.


다음에 설명하는 SequenceService가 이번 포스팅의 핵심 내용이다.


 SequenceService

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
package com.apress.springrecipes.sequence;
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
 
@Service
public class SequenceService {
    @Autowired
    private SequenceDao sequenceDao;
    
    public SequenceService() {}
    
    public SequenceService(SequenceDao sequenceDao) {
        this.sequenceDao = sequenceDao;
    }
    
    public void setSequenceDao(SequenceDao sequenceDao) {
        this.sequenceDao = sequenceDao;
    }
    
    public String generate(String sequenceId) {
        Sequence sequence = sequenceDao.getSequence(sequenceId);
        int value = sequenceDao.getNextValue(sequenceId);
        return sequence.getPrefix() + value + sequence.getSuffix();
    }
}
 
cs

 

 SequenceService 클래스는 서비스 클래스로, DAO(Data Access Object)와 연동하여 요청을 처리해주는 클래스다. (이런 구현 방법이 실제로 자주 쓰인다.)

 @Service 어노테이션이 붙어있기 때문에 SequenceService는 스프링 빈으로 등록되고, @Autowired가 있기 때문에 sequenceDao 필드엔 이에 호환되는 SequenceDaoImpl 클래스가 자동으로 연결된다. 그렇기 때문에 Main 클래스에서 sequenceService.sequenceDao 필드를 초기화하는 내용이 없음에도 generate() 메소드를 호출해도 오류가 생기지 않는 것이다.


배열, 맵 연결

 배열형 프로퍼티에 @Autowired를 붙이면 스프링은 매치되는 모든 빈을 찾아서 연결하고, type-safe한 컬렉션에 @Autowired를 붙였을 때도 호환되는 모든 빈을 찾아서 연결된다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class SequenceGenerator{
 
    @Autowired
    private PrefixGenerator[] prefixGenerators;
}
 
 
public class SequenceGenerator{
    @Autowired
    private List<PrefixGenerator> prefixGenerators;
}
 
public class SequenceGenerator{
    @Autowired
    private Map<String, PrefixGenerator> prefixGenerators;
}
 
 
cs


 이와 같은 경우 prefixGenerators에 호환되는 모든 빈들이 연결된다.



2. @Autowired로 POJO 메소드와 생성자 연결


 @Autowired는 POJO Setter 메서드, 생성자에도 적용할 수 있다.

 그렇게 되면 세터 메서드와 생성자의 패러미터로 들어오는 변수에 호환되는 빈이 자동으로 연결된다. (패러미터는 여러개 있어도 가능하다)


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
package com.apress.springrecipes.sequence;
 
import org.springframework.beans.factory.annotation.Autowired;
 
public class SequenceGenerator {
    private PrefixGenerator prefixGenerator;
    private String suffix;
    private int initial;
    private int counter;
 
    public SequenceGenerator() {
    }
 
    public SequenceGenerator(PrefixGenerator prefixGenerator, String suffix, int initial) {
        this.prefixGenerator = prefixGenerator;
        this.suffix = suffix;
        this.initial = initial;
    }
 
    //@Autowired
    public void setPrefixGenerator(PrefixGenerator prefixGenerator) {
        this.prefixGenerator = prefixGenerator;
    }
 
    @Autowired
    public void myOwnCustomInjectionName(PrefixGenerator prefixGenerator) {
        this.prefixGenerator = prefixGenerator;
    }
 
    public void setSuffix(String suffix) {
        this.suffix = suffix;
    }
 
    public void setInitial(int initial) {
        this.initial = initial;
    }
 
    public synchronized String getSequence() {
        StringBuilder builder = new StringBuilder();
        builder.append(prefixGenerator.getPrefix());
        builder.append(initial + counter++);
        builder.append(suffix);
        return builder.toString();
    }
}
 
cs


 위에서 Setter 메소드에 @Autowired 어노테이션을 붙여 prefixGenerator에 매칭되는 빈 오브젝트를 자동으로 찾아서 집어넣는다.


 스프링에서 기본적으로는 @Autowired를 붙인 프로퍼티는 필수 프로퍼티로 취급되어, 매칭되는 빈이 없으면 예외가 발생한다. @Autowired의 required 속성값을 false로 지정하면 선택적 프로퍼티로 취급되어 빈을 못 찾아도 그냥 지나친다.

1
2
3
4
5
6
7
8
public class SequenceGenerator {
    ...
    @Autowired(required = false)
    public void myOwnCustomInjectionName(PrefixGenerator prefixGenerator) {
        this.prefixGenerator = prefixGenerator;
    }
}
 
cs



3. 모호한 자동 연결 명시하기


@Primary

 스프링에서는 @Primary를 붙여 후보 빈을 명시한다. 여러 빈이 자동 연결 대상일 때, 특정한 빈에 우선권을 부여하는 것이다.


@Qualifier

 @Qualifier에 이름을 줘서 후보 빈을 명시할 수도 있다.


4. 여러곳에 분산된 POJO 참조 문제 해결


 어플리케이션 규모가 커지면 모든 POJO 설정을 하나의 자바 구성 클래스에 담기 어렵기 떄문에, 보통 POJO 기능에 따라 여러 자바 구성 클래스로 나눠서 관리한다. 그런데 자바 구성 클래스가 여러개 있으면 서로다른 클래스에 정의된 POJO를 자동으로 연결하거나 참조하기가 쉽지 않다.

 이는 다음의 두가지 방법으로 해결 가능하다.


 방법1. 자바 구성 클래스가 위치한 경로마다 어플리케이션 컨텍스트를 초기화

1
2
3
AnnotationConfigApplication context = new AnnotationConfigApplicationContext(PrefixConfiguration.class
                                                            SequenceGeneratorConfiguration.class)
 
cs



 방법2. @Import로 구성 파일을 나눠서 임포트

1
2
3
4
5
6
@Configuration
@Import(PrefixConfiguration.class)
public class SequenceConfiguration{
    ....
 
}
cs


 

4. 레퍼런스

스프링 5 레시피(4판) - 한빛 미디어


'IT 관련 > 스프링(스프링5 레시피)' 카테고리의 다른 글

스프링 프로젝트 생성  (0) 2020.02.23
Chap 2.1 자바로 POJO 구성하기  (0) 2020.02.23
스프링 시작(Chapter1)  (0) 2020.02.20
블로그 이미지

서기리보이

,

기본적인 스프링 프로젝트 생성에서부터 막힐 수 있기 때문에 미리미리 정리...

 

 

STS 상단의 File - new - Project... 클릭

Spring Starter Project 선택.

프로젝트 명, Type(그레이들/Maven 선택) 값 설정. 나머지 값들은 기본으로 두면 됨.

그 다음 Next-Next-Finish 하면 프로젝트 생성.

 

불필요한 파일들 삭제(빨간 네모 부분)

프로젝트에서 불필요한 파일들을 삭제하고, src/main/java 디렉토리에 필요한 코드를 작성하면 된다.

 

 

 

 

 

 

 

블로그 이미지

서기리보이

,

0. 들어가기에 앞서


 이번 장의 주제는 스프링의 주요 기능이다.

 IoC(Inversion of Control. 제어의 역전)은 스프링 프레임워크의 심장부라고 할 수 있다. IoC 컨테이너는 POJO(Plain Old Java Object. 오래된 방식의 단순 자바 객체)를 구성하고 관리하는데, 스프링 프레임워크의 가장 중요한 의의가 이 POJO로 자바 애플리케이션을 개발하는 것이므로 스프링의 주요 기능은 대부분 IoC 컨테이너 안에서 POJO를 구상하고 관리하는 일과 연관되어있다.


* '빈(bean)' = 'POJO 인스턴스'

* '컴포넌트(component)' = 'POJO 클래스'



1. 자바로 POJO 구성하기


목적

스프링 IoC 컨테이너에서 어노테이션을 붙여 POJO 관리하기.


해결책

 @Configuration, @Bean을 붙인 자바 구성 클래스를 만들거나, @Component, @Repository, @Service, @Controller 등을 붙인 자바 컴포넌트를 구성하여 POJO 클래스를 설계한다.

 IoC 컨테이너는 이렇게 어노테이션(annotation)을 붙인 자바 클래스를 스캐닝하여 어플리케이션의 일부인 것 처럼 POJO 인스턴스/빈을 구성한다.


예제

 순차번호 생성기(다목적 시퀀스 생성기) 어플리케이션 개발을 위해 SequenceGenerator를 다음과 같이 구현한다.

 Sequence는 prefix, suffix, initial 세가지 프로터피를 가지고, getSequence() 메소드를 호출하면 이 세가지 값이 조합된 시퀀스가 리턴된다.


SequenceGenerator

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
package com.apress.springrecipes.sequence;
 
import java.util.concurrent.atomic.AtomicInteger;
 
public class SequenceGenerator {
 
    private String prefix;
    private String suffix;
    private int initial;
    private final AtomicInteger counter = new AtomicInteger();
 
    public SequenceGenerator() {
    }
 
    public void setPrefix(String prefix) {
        this.prefix = prefix;
    }
 
    public void setSuffix(String suffix) {
        this.suffix = suffix;
    }
 
    public void setInitial(int initial) {
        this.initial = initial;
    }
 
    public String getSequence() {
        String builder = prefix +
                initial +
                counter.getAndIncrement() +
                suffix;
        return builder;
    }
}
cs


 이제 @Configuration, @Bean 어노테이션을 이용하여 자바 POJO를 생성하는 클래스 SequenceGeneratorConfiguration을 다음과 같이 구현한다.


SequenceGeneratorConfiguration

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.appress.springrecipes.sequence.config;
 
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
 
import com.apress.springrecipes.sequence.SequenceGenerator;
 
//@Configuration : 이 클래스가 구성클래스임을 알림
@Configuration
public class SequenceGeneratorConfiguration {
    //@Bean : @Configuration 어노테이션이 달린 클래스의 빈(Bean) 인스턴스 정의부
    @Bean
    public SequenceGenerator sequenceGenerator() {
        SequenceGenerator seqgen = new SequenceGenerator();
        seqgen.setPrefix("30");;
        seqgen.setSuffix("A");
        seqgen.setInitial(100000);
        
        return seqgen;
    }
}
 
cs



 스프링은 @Configuration이 달린 구성 클래스를 보면 일단 그 안의 빈(Bean)인스턴스 정의부, 즉 @Bean을 붙인, 빈 인스턴스를 생성해 반환하는 메서드를 찾는다.


 구성 클래스의 메소드에 @Bean을 붙이면 빈이 생성되는데 @Bean의 name 속성값을 지정해 빈 이름을 지정해 줄 수 있다.(ex : @Bean(name="mys1") -> 생성되는 빈 이름은 mys1으로 생성됨.). name 속성값을 지정해주지 않으면 원본 메소드 이름과 같은 이름의 빈이 생성된다.


 

 다음 단계로 어노테이션을 붙인 자바 클래스를 스캐닝하기 위해 IoC 컨테이너를 인스턴스화해야한다.


 스프링에서 제공하는 IoC 컨테이너는 기본 구현체인 Bean Factory, 그리고 이와 호환되는 고급 구현체인 Application Context 두가지가 있다.


 둘은 각각 빈 팩토리, 어플리케이션 컨텍스트에 접근하기위한 인터페이스다. 그리고 Application Context는 BeanFactory의 하위 인터페이스라서 호환성이 보장된다.


 * Application Context는 기본 기능에 충실하면서도 Bean Factory보다 발전된 기능을 가지고 있으니, 리소스 제약을 받는 상황이 아니라면 Application Context를 사용하는게 좋다.


 ApplicationCotext는 인터페이스이므로, 사용하려면 구상 클래스(concrete class)가 필요하다. 스프링에서 지원하는 ApplicationContext의 몇가지 구상체 중에서, AnnotationConfigApplicationContext가 가장 최근 작품이고, 유연하기 때문에 사용이 권장된다.


 이제 Main클래스를 통해 시퀀스 생성기를 실행하는 것을 보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.apress.springrecipes.sequence;
 
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
 
import com.appress.springrecipes.sequence.config.SequenceGeneratorConfiguration;
 
public class Main {
    public static void main(String[] args){
        //ApplicationContext를 구현한 구상 클래스들 중 AnnotationConfigApplicationContext 사용
        ApplicationContext context = new AnnotationConfigApplicationContext(SequenceGeneratorConfiguration.class);
        
        //getBean() 메소드로 구성 클래스에 선언된 빈을 가져옴
        SequenceGenerator generator = context.getBean(SequenceGenerator.class);
        
        System.out.println(generator.getSequence());
        System.out.println(generator.getSequence());
    }
}
cs


2. POJO 클래스에 @Component를 붙여 DAO 빈 생성하기


 지금까지는 구성클래스 값을 하드코딩해서 스프링 빈을 인스턴스화했다.

 실제로 POJO는 대부분 DB나 유저 입력을 이용해 인스턴스화한다. 이번에는 더 현실적인 시나리오로, Domain 클래스와 DAO(Data Access Object)를 이용해 POJO을 생성한다.

 DB를 실제로 구현하진 않고, DAO 클래스에 하드코딩할 것이다. (이런 구조가 추후에 많이 사용된다.)


 앞에 나온 시퀀스 생성기에 도메인 클래스, DAO 패턴을 적용하기 위해 클래스 구조를 바꿔야한다. 

 우선 Sequence 도메인 클래스를 만든다.


Sequence

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
package com.apress.springrecipes.sequence;
 
public class Sequence {
    private String id;
    private final String prefix;
    private final String suffix;
    
    public Sequence(String id, String prefix, String suffix) {
        this.id = id;
        this.prefix = prefix;
        this.suffix = suffix;
    }
    
    public String getId() {
        return id;
    }
    
    public String getPrefix() {
        return prefix;
    }
    
    public String getSuffix() {
        return suffix;
    }
}
 
cs



 다음은 DB 데이터 액세스를 처리하는 DAO 인터페이스와 이를 구현한 구상 클래스를 보자.

 

 SequenceDao

1
2
3
4
5
6
7
8
9
10
package com.apress.springrecipes.sequence;
 
public interface SequenceDao {
    //DB 테이블에서 sequenceId로 POJO나 Sequence 객체를 로드함
    Sequence getSequence(String sequenceId);
    
    //다음 시퀀스 값을 얻어옴
    int getNextValue(String sequenceId);
}
 
cs


SequenceDaoImpl

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
package com.apress.springrecipes.sequence;
 
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
 
import org.springframework.stereotype.Component;
 
@Component("sequenceDao")
public class SequenceDaoImpl implements SequenceDao {
    private final Map<String, Sequence> sequences = new HashMap<>();
    private final Map<String, AtomicInteger> values = new HashMap<>();
    
    //생성자에서 데이터베이스를 하드코딩으로 구현함
    public SequenceDaoImpl() {
        sequences.put("IT"new Sequence("IT""30""A"));
        values.put("IT",  new AtomicInteger(10000));
    }
    
    public Sequence getSequence(String sequenceId) {
        return sequences.get(sequenceId);
    }
    
    public int getNextValue(String sequenceId) {
        AtomicInteger value = values.get(sequenceId);
        return value.getAndIncrement();
    }
}
 
cs



 SequenceDaoImpl 클래스에 @Component("sequenceDao")를 붙이면 스프링은 이 클래스를 이용해 POJO를 생성한다. @Component에 넣은 값("sequenceDao")으로 빈 이름으로 할당된다.

 @Component는 스프링이 발견할 수 있게 POJO에 붙이는 범용 어노테이션이다. 스프링에는 Persistent(영속화), Service(서비스), Presentation(표현)의 세 레이어가 있는데, @Repository, @Service, @Controller가 각각 이 세 레이어를 가리키는 어노테이션이다.


 POJO의 쓰임새가 명확하지 않을 땐 그냥 @Com[onent를 붙여도 되고, 특정 용도에 맞게 쓰려면 구체적으로 명시하는 편이 좋다.(ex: @Repository는 발생한 예외를 DataAccessException으로 감싸 던지기 때문에 디버깅시 유리함)


스캐닝 커스터마이징

 기본적으로 스프링은 @Configuration, @Bean, @Component, @Repository, @Service, @Controller가 달린 클래스를 모두 감지하는데, 필터를 적용하여 스캐닝 과정을 커스터마이징하면 원하는 어노테이션만 골라서 컨텍스트에 포함시키거나 제외할 수 있다.

 모든 패키지를 스캐닝하면 시동 과정이 쓸데없이 느려 질 수 있어서 유용하다.


 다음 예시와 같이 필터를 적용 할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.apress.springrecipes.sequence.config;
 
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
 
@Configuration
@ComponentScan(
        includeFilters = { 
                @ComponentScan.Filter(
                        type = FilterType.REGEX, 
                        pattern = {"com.apress.springrecipes.sequence.*Dao""com.apress.springrecipes.sequence.*Service" }
                }, 
        excludeFilters = {
                @ComponentScan.Filter(
                        type = FilterType.ANNOTATION, 
                        classes = {org.springframework.stereotype.Controller.class }
                }
        )
 
public class SequenceGeneratorConfiguration {
 
}
 
cs



 스프링에서 지원하는 필터 표현식은 네종류다. 

- annotation : 필터 대상 어노테이션 타입 지정

- assignable : 필터 대상 클래스/인터페이스 지정

- regex : 정규 표현식을 이용한 클래스 매칭으로 대상 클래스 지정

- aspectj : AspectJ 포인트컷 표현식으로 클래스를 매칭하여 대상 클래스 지정


 위 예시에서는 regex, annotation 표현식을 이용해 필터링을 적용하였다.

 includeFilters에서 com.appress.springrecipes.sequence.*Dao/*Service 형태의 이름을 가진 클래스를 포함하도록 했기 때문에, 어노테이션이 달려있지 않아도 스프링이 자동 감지한다.

 excludeFilters에서는 Controller 클래스를 지정해 제외시켰다.


 테스트는 다음의 코드로 진행한다.


 Main

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.apress.springrecipes.sequence;
 
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
 
public class Main {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext("com.apress.springrecipes.sequence");
        
        SequenceDao sequenceDao = context.getBean(SequenceDao.class);
        
        System.out.println(sequenceDao.getNextValue("IT"));
        System.out.println(sequenceDao.getNextValue("IT"));
    }
}
 
cs



3. 레퍼런스

스프링 5 레시피(4판) - 한빛 미디어




블로그 이미지

서기리보이

,

1. 환경 구축


 스프링 프레임워크를 이용한 개발을 위한 개발툴(IDE)에는 이클립스, STS, 인텔리제이, 메모장 등을 사용할 수 있는데, 글쓴이는 STS를 이용한다.


 최신 STS4 버전을 이용하고, jdk 1.8.0_241 버전을 이용한다.


 설치방법은 다음의 '나 연구소'라는 블로그에 정리가 잘 되어있으니 (링크)를 참조하면 좋을 듯 하다.


 설치를 완료하고, SpringToolSuite4.exe 를 실행하면 이클립스와 거의 똑같은 화면이 뜬다.



2. 메이븐 프로젝트 임포트 및 실행

 

 STS4를 실행하면 다음과 같은 화면이 뜬다.


 여기서 Create new Spring Starter Project를 클릭하고, 그 후 설정은 이번에는 모두 기본값으로 두고 진행한다.


 이후 File - Import 을 클릭하고, Maven/Existing Maven Project 옵션을 선택한다.


 이후 디렉토리 설정에서 ch01/springintro_mvn 디렉토리를 지정한다.



 여기서 Import Maven Projects 창에서 pom.xml com.apress.springrecipes... 이 내용이 보이면 프로젝트가 정상적으로 임포트 된 것이다.


 이 상태가 됬다면 Finish하면 된다.


 이후 프로젝트를 선택하고 RunAs - Maven Build 옵션을 선택하고, 팝업창이 뜨면 Run하면 빌드된다.


 이제 F5를 눌러 프로젝트 디렉토리를 갱신해보면 하위 디렉토리에 target 디렉토리사 새로 생겨있다. 이 디렉토리 아래 springintro_mvn-4.0.0-SNAPSHOT.jar 파일을 우클릭하고 Run as - Run configurations 옵션을 선택하면 실행 설정을 편집하는 팝업이 뜬다.


여기서 좌측에 Java application을 선택하고, Main Class 박스에 이 프로젝트의 메인 클래스 com.apress.springrecipes.hello.Main을 입력한다.(탐색 기능을 쓰면 정확히 찾을 수 있음)


 이제 Run 버튼을 클릭하면 인사말이 뜬다.

실행 결과



3. 그레이들 프로젝트 임포트 및 실행


 그레이들은 메이븐보다 최근에 나온 빌드 스크립트로, 조만간 메이븐을 대체할 예정이다.


 STS에서 그레이들을 사용하려면 우선 Buildship 확장 플러그인을 설치해야한다.

 STS상단 메뉴에서 Help - Eclipse Marketplace 를 선택하고, Gradle 키워드로 검색한다.

 (책에서는 2.0버전을 설치하라고 되어있지만 현재는 3.0버전밖에 없음)


 이제 예제 소스를 임포트하는 단계다.

 File - Import 에서 Gradle/Existing Gradle Projects 옵션으로 넘어간다.

 디렉토리는 예제 소스의 ch01/springintro 디렉토리로 설정한다.




 이후 프로젝트를 선택하고 Run 하면 실행된다.


 책에서는 프로젝트를 빌드하고, libs 디렉터리의 springintro.jar 파일을 선택에 Run configurations 설정에서 Main class를 설정해줘야한다고 하는데 이 과정이 필요없이 바로 실행이 됬다.

 빌드쉽 버전 차이 때문에 그런게 아닐까 싶다.


 배움이 즐거운 개발자님의 포스팅을 통해 빌드하는 법을 찾았다.(링크)

 

 우선 STS에서 Gradle Tasks 뷰를 띄워야한다.

 우측 상단에 커맨드 검색 기능을 이용해 Show View(Gradle Tasks) 커맨드를 선택하여 Gradle Tasks 뷰를 띄우고, 해당 프로젝트의 build/jar를 더블 클릭하면 빌드된다.


 그러면 실제 디렉토리에 다음과 같이 jar 파일이 생성되어있다.

 



 

3. 빌드 스크립트


 자바 애플리케이션을 개발하다보면 JAR 파일이나 구성 파일 복사, 컴파일에 필요한 클래스패스 설정, JAR 의존체 다운로드 등 자잘한 작업이 많은데, 자바 빌드 툴이 이를 대신 처리한다.

 빌드 파일만 있으면 빌드시 원 개발자가 의도한 작업을 그대로 재현할 수 있기 때문에 자바 빌드 툴은 중요하다. 메이븐 pom.xml, 그레이들 build.gradle 파일만 있으면 빌드 툴에 무관하게 다른 유저, 다른 시스템에서도 일관되게 빌드할 수 있다.

 자바 개발자들은 대부분 빌드 스크립트로 메이븐 pom.xml이나 이보다 나중에 나온 build.gradle을 사용한다.

 이 부분은 아직 빌드 스크립트를 본 적 없어서 이해가 가지 않을 수 있으니 일단 넘어가자.


4. 레퍼런스

스프링 5 레시피(4판) - 한빛 미디어

https://m.blog.naver.com/PostView.nhn?blogId=pyj721aa&logNo=221613874213&proxyReferer=https%3A%2F%2Fwww.google.com%2F - STS 설치

https://github.com/nililee/spring-5-recipes - 스프링 예제

https://galid1.tistory.com/500 - 그레이들 빌드 하는 법.

블로그 이미지

서기리보이

,

MQTT 프로토콜

IT 관련/기타 2019. 11. 20. 17:36

1. Mqtt 프로토콜이란?


Message Queue for Telemetry Transport


 발행-구독 기반 메시징 프로토콜로, 보통 TCP/IP 기반으로 구현된다.



2. 구조

사진 출처 : https://www.segger.com/products/security-iot/emmqtt/

 

 Mqtt는 하나의 브로커 서버와 다수의 클라이언트로 구성된다.


 위 그림에서 보듯이 냉장고, 스마트폰, PC 등이 클라이언트로 브로커 서버와 연결하고, 브로커 서버는 클라이언트들과 데이터를 주고 받는다.


 한 클라이언트가 메시지를 Publish하면 해당 메시지의 Topic을 Subscribe해둔 클라이언트들에게 메시지가 전달된다.


 클라이언트는 브로커 서버에게 어떤 Topic을 Subscribe하여 관심있는 메시지를 받아 볼 수 있다.


3. 특징


1) 발행-구독 기반

2) 가볍다

3) 1서버 - 다수의 클라이언트 구조

4) 3단계의 QoS 지원

5) 다양한 개발언어, 다양한 클라이언트 지원



1) 발행(Publish) - 구독(Subscribe) 기반

 Mqtt 프로토콜에서 주고받는 메시지는 (Topic + 메시지)로 구성되고, 각 클라이언트들은 특정 토픽들을 구독(Subscribe) 할 수 있다.

 한 클라이언트가 메시지를 발행(publish)하면 메시지의 Topic을 구독한 클라이언트들은 모두 그 메시지를 받고, 구독하지 않은 클라이언트들은 메시지를 받지 못한다.


2) 가볍다.

 Mqtt 프로토콜은 주고받는 메시지의 크기가 작기 때문에 IoT 디바이스 등 대역폭이 제한되는 디바이스에서 사용하기 좋다.


3) 1서버 - 다수의 클라이언트 구조

 Mqtt 프로토콜은 하나의 서버에 다수의 클라이언트가 연결하는 구조를 가진다.


4) 3단계의 QoS 지원

 Qos(Quality of Service).

 Mqtt 프로토콜은 0,1,2 세단계의 QoS를 지원한다.


 0 (fire and foget) 

 최대 1회 전송. Topic을 동해 메시지를 전송하고 클라이언트가 메시지를 제대로 받았는지 보장하지 않는다.


 1 (acknowledged delivery)

 최소 1회 전송. 구독하는 클라이언트가 메시지를 받았는지 불확실하면 정해진 횟수만큼 재전송한다.(중복 전송의 위험성 존재)


 2 (assured delivery)

등록된 클라이언트는 요구된 메시지를 정확이 한 번 수신할 수 있게 보장한다.


5) 다양한 개발언어, 다양한 클라이언트 지원

 C, 자바, Node.js, 파이썬 등 다양한 개발언어로 Broker/Client를 개발할 수 있는 라이브러리가 존재한다.


4. 레퍼런스

http://woowabros.github.io/experience/2017/08/11/ost_mqtt_broker.html

https://en.wikipedia.org/wiki/MQTT

http://mqtt.org/

'IT 관련 > 기타' 카테고리의 다른 글

MIT 라이센스  (2) 2019.09.26
블로그 이미지

서기리보이

,

1. ER (Entity & Attributes)


Entity(엔티티)

 ER 모델링의 기본 컨셉이다.

 엔티티는 mini world의 특정 사물이다 오브젝트를 나타낸다. (사람, 차, 집 등 물리적인 것이나, 회사, 직업, 강의 등 개념적인 것)


Attribute(속성)

 어트리뷰트는 엔티티의 특성을 나타낸다. 이름, 주민번호, 집주소, 성별 등을 어트리뷰트로 쓸 수 있다.


2. Attribute


Simple(Atomic) Attribute

 atomic한 하나의 value를 갖는 어트리뷰트다. 주민등록번호, 주민번호 등이 여기에 해당 할 수 있다.


Composite Attribute

 여러 어트리뷰트가 합쳐진 어트리뷰트다. 주소(아파트 동번호 + 아파트 호수 + 지역구 명 + 도시 명 + 국가 명), 이름(First name, Last name) 등이 있을 수 있다.


Multi-valued Attribute

 배열처럼 여러개의 값을 가지는 어트리뷰트다. 

 이전 학력(초등학교, 중학교, 고등학교, 대학교...) 등이 있을 수 있다.


 Composite + Multi-valued 어트리뷰트도 있을 수 있고, Composite, Multi-valued 어트리뷰트는 다양한 레벨에 존재 할 수 있다.(Composite 어트리뷰트를 여럿 가지는 Multi-valued 어트리뷰트, Multi-valued 어트리뷰트를 가지는 Composite 어트리뷰트 등)


2.1 Stored Attribute VS Derived Attribute


Stored Attribute

 값이 저장된 어트리뷰트.


Derived Attribute

 Stored Attribute로부터 도출될 수 있는 어트리뷰트.


 ex) 직원들 개개인의 정보만 저장된 DB에서, 직원의 나이는 Stored Attribute, 직원들의 숫자는 Derived Attribute.




3. Entity Type and Entity Set


Entity Type(intension)

 같은 어트리뷰트들을 가진 엔티티들의 집합. 스키마 같은 개념적인 것.


Entity Set(extension)

 어떤 엔티티 타입의 모든 엔티티들의 집합.


 이름 그대로 엔티티 타입은 타입, Set은 엔티티 인스턴스들의 집합이다.




4. Key Attribute(키 속성)


 어떤 엔티티 타입에서 각각의 엔티티가 unique한(서로다른 값을 가지는) 값을 가져야하는 어트리뷰트를 Key Attribute라고 부른다.



5. Value Sets(Domain)


 모든 Simple 어트리뷰트들은 특정 Value Set(Domain)과 연관된다.

 예를 들어 MM-DD-YYYY 형식의 Date라는 어트리뷰트의 M,D,Y는 모두 정수이고, 나이 어트리뷰트는 16~70 범위의 도메인을 가진다.

 

 Value Set은 수학적으로 다음과 같이 표현한다.

 

 P(V) : Power Set of V

 A(e) : 엔티티 e의 어트리뷰트 A 값

 

 이 정의는 싱글 밸류 어트리뷰트, 멀티 밸류 어트리뷰트에 모두 적용될 수 있다.


 컴포지트 어트리뷰트는 다음처럼 표현한다.



 V1,V2,...,Vn은 A의 Simple Component Attrubute의 Value Set이다.



6. ER 다이어그램


 다이어그램 구성요소



다이어그램 예시


7. Relationship


Relationship은 둘 이상의 엔티티를 연관시킨다.


ex)

 EMPLOYEE 홍길동 works on the SpaceX PROJECT

 EMPLOYEE 안철수 works for the H&R DEPRATMENT


 n개의 엔티티 E_1, E_2, E_3,... E_n 타입들 사이의 Relationship set R(관계집합)은 E_j(1 <= j <=n)에 속하는 엔티티 n개(e_1, e_2, ... , e_n)와 관계를 갖는 r_i의 집합이다.


7.1 Relation Type의 Degree

 왼쪽의 WORKS_ON Relationship은 두 가지 엔티티를 관계시키므로 Degree가 Binary고, 오른쪽의 SUPPLY 엔티티는 세가지 엔티티를 관계시키므로 Degree가 Ternary라고 한다.



7.2 Cardinality Ratio


 Cardinality Ratio는 한 엔티티가 Relationship 인스턴스에 참여할 수 있는 최대 숫자를 나타낸다.

 1:1, 1:N, N:1, M:N 이 있다.

 

1 : 1

N:1


 왼쪽 그림은 EMPLOYEE 하나당 DEPARTMENT 하나와 관계를 맺는 1:1관계, 오른쪽 그림은 DEPARTMENT 하나당 EMPLOYEE N개가 관계를 맺는 N:1 관계의 예시다.


7.3 Binary Relationship의 Structural Constraint


 Binary Relationship에는 다음과 같은 구조적 제약사항들이 존재한다.


Participation Constraint

 엔티티가 존재하려면 또다른 엔티티와 관계되어야한다. Total Participation이든 Partial Participation이든 말이다.



8. Weak Entity

 

 위크 엔티티 타입은 자기 자신을 위한 키 어트리뷰트를 가지지 않는 엔티티 타입을 말한다.

 자기 자체로는 존재 할 수 없고, 다른 엔티티와의 Partial Relationship 관계를 통해서만 존재할 수 있다.


 ER 다이어그램에서는 다음과 같이 표현한다.

블로그 이미지

서기리보이

,

REST API

기타 2019. 11. 15. 14:47

1. REST API(RESTFul API)란


 Representational State Transfer API.


 REST API에 대해서 간략히만 말하면, GET, POST, DELETE, PUT의 4가지 메소드를 제공하는 API 구조를 말한다.

 주로 웹 서버를 구현 할 때, 리소스 조회 - GET, 리소스 생성 - POST, 리소스 삭제 - DELETE, 리소스 수정 - PUT 메소드로 외부에서 리소스에 접근 가능하게 한다.


 이정도만 알고, 서버 구현용 언어(자바스크립트 등) 하나만 사용할 줄 알아도 기본적인 REST API 서버 기능 구현까지는 가능하다.


2. REST API 특징


1) 클라이언트-서버 구조

2) 인터페이스 일관성(Uniform interface)

3) 무상태성(Stateless)

4) 캐시 사용 가능

5) 계층형 구조(Layered System)

6) Code On Demand

7) 자체 표현구조(Self-describing)


1) REST API는 기본적으로 클라이언트와 서버가 분리된 클라이언트-서버 구조로 디자인된다.


2) 서버와 클라이언트가 일관된 인터페이스를 따르게 하여 클라이언트와 서버를 서로 떨어뜨려 놓는다. 이에 따라 서버와 클라이언트는 서로 독립적으로 발전 될 수 있다.


3) 세션, 쿠키 등 상태를 저장하는 방법을 사용하지 않는다. 상태가 없기 때문에 REST API 호출은 서로 독립적으로 수행되고, 각각의 호출에는 작업을 완수하기 위해 필요한 데이터가 모두 포함되어야한다.


4) 무상태성 때문에 Request의 크기가 커질 수 있기 때문에, 오버헤드 최소화를 위해 캐시를 사용할 것을 권장한다.


5) REST 서버는 다중 계층으로 구성 될 수 있다. 보안, 로드밸런싱, 암호화 계층을 추가하는 등 구조상의 유연성을 둘 수 있고, PROXY, 게이트웨이같은 네트워크 기반의 중간 메체를 사용할 수도 있는 것이다. 


6) Code On Demand. 필요에 따라 자바스크립트나 Applet 등 서버의 일부 코드가 클라이언트에게 제공 될 수 있다.


7) REST API는 메시지만 보고도 쉽게 이해할 수 있는 자체 표현적 구조를 가지고 있다.


 이 정도가 REST API의 특징들이다.

 사람들마다 6번이나 7번 특징은 제외시키는 경우도 있는데, REST API에 대해서 설명할 때는 이 7가지 정도만 알면 충분하다.



3. 디자인 가이드


1) URL은 정보의 자원을 표현해야한다.(리소스명은 동사보다는 명사를 사용)

2) 자원에 대한 행위를 HTTP 메소드(GET, POST, PUT, DELETE)로 표현한다.


1 ex)

GET http://192.168.0.63:1337/members/1

DELETE http://192.168.0.63:1337/members/1


2 ex)

회원 정보 조회 : GET members/1

회원 추가 : POST /members/2 (회원 정보는 request의 body 부분에 포함시킵니다.)

회원 삭제 : DELETE /members/1

회원 정보 업데이트 : DELETE /members/1 (수정 정보는 request의 body 부분에 포함시킵니다.)


 참고로 각각의 메소드를 섞어서 쓰는 것도 가능하긴 한데, 원래의 목적대로 사용하는 것이 올바른 디자인 방식이다.

 GET - 리소스 조회

 POST - 리소스 생성

 PUT - 리소스 수정

 DELETE - 리소스 삭제


 특히 GET, POST를 섞어서 쓰는 경우가 있는데, GET은 request의 body가 없기 때문에 URL로만 데이터를 표현한다. 그래서 전송가능한 데이터 크기에 제한이 있고, 기타 메소들마다 차이가 있으니 제역할에 맞게 사용하도록 하자.



4. REST의 3요소


- 리소스(URL)

- 메소드(GET, POST, PUT, DELETE)

- 메시지




5. 장점


- HTTP 프로토콜을 그대로 활용 할 수 있다.

- 유연하다.


 REST API 구조는 HTTP 프로토콜을 그대로 활용하기 때문에 HTTP 프로토콜을 기반으로 REST API 서버를 구현하기 위해서 추가적인 라이브러리나 소프트웨어를 설치할 필요가 없다.(GET, POST, PUT, DELETE 메소드 모두 HTTP 메소드의 일종이다.)


 REST API 구조를 이용하면 다양한 포멧의 데이터를 리턴할 수 있어 유연하고, 계층적 구조를 가지기 때문에 서버 구조 구현에도 좀 더 유연함을 가지고 있다.



6. 레퍼런스

https://www.mulesoft.com/resources/api/restful-api

https://gmlwjd9405.github.io/2018/09/21/rest-and-restful.html

https://ko.wikipedia.org/wiki/REST

https://ko.wikipedia.org/wiki/HTTP

https://meetup.toast.com/posts/92




블로그 이미지

서기리보이

,

1. 이터레이터 패턴이란

1.1 정의

 반복자를 사용하여 컨테이너를 가로지르며 컨테이너의 요소들에 접근하는 디자인 패턴


 이터레이터 패턴을 사용하면 집합체 내에서 어떤 식으로 일이 처리되는지 몰라도 그 안에 들어있는 항목들에 대해서 반복작업을 수행 할 수 있다.

 예를 들어, 집합체가 C++의 vector이든, list이든 상관 없이 같은 코드로 반복 작업을 수행할 수 있게 하는 것이다.



1.2 구조




 여기서 Client는 Aggregate라는 집합체 클래스에 대해 어떤 반복 작업을 수행하려고 한다.

 반복 작업은 Aggregate의 createIterator() 메소드로 Iterator를 생성해서수행한다.


 Iterator의 메소드들은 조금 다르게 구성되는 경우도 있다. 이 다이어그램에서 Iterator는 java.util.Iterator이고 C++의 Iterator의 경우 ++, -- 연산자를 사용할 수 있고, begin(), end() 등의 메소드를 포함한다.

 중요한 것은 어떤 구조이든지간에 집합체 내부 구조와 상관없이 반복 작업을 수행할 수 있다는 것이다.



2. 장점

- 집합체 클래스의 응집도를 높여준다.

- 집합체 내에서 어떤 식으로 일이 처리되는지 알 필요 없이, 집합체 안에 들어있는 모든 항목에 접근 할 수 있게 해준다.


 응집도는 클래스나 모듈이 특정 목적이나 역할을 얼마나 일관되게 지원하는지를 나타내는 척도다.

 이터레이터 패턴을 사용하면 원래 클래스의 역할(집합체 관리)에서 반복 작업이라는 다른 역할을 분리시켜 응집도를 높일 수 있고, 그 덕에 컬랙션 변경에 의한 클래스의 변경, 반복자 기능 변경에 의한 클래스 변경을 방지할 수 있다.


 이터페이터 패턴을 사용하면 집합체 내에서 어떤 식으로 일이 처리되는지 알 필요 없이 반복작업을 수행할 수 있다.

 집합체가 ArrayList로 이루어져있든, List로 이루어져있든 상관없이 똑같은 코드로 반복 작업을 수행 할 수 있는 것이다.


3. 레퍼런스

- Head First Design Pattern(O'REILLY media)

https://ko.wikipedia.org/wiki/%EB%B0%98%EB%B3%B5%EC%9E%90_%ED%8C%A8%ED%84%B4

https://www.geeksforgeeks.org/iterators-c-stl/ : C++ iterator example






블로그 이미지

서기리보이

,