URI

 

URI (Uniform Resource Identifier) : 

Uniform  = 리소스 식별하는 통일된 방식

Resource = 자원, URI로 식별할 수 있는 모든 것

Identifier = 다른 항목과 구분하는데 필요한 정보

 

 

URI? URL? URN?

URI는 로케이터(locator), 이름(name) 또는 둘 다 추가로 분류될 수 있다.

 

즉, URI는 가장 큰 개념으로 생각하면 된다.

URL, URN을 포함한다.

 

URL = 리소스가 있는 위치를 지정

URN = 리소스에 이름을 부여

 

URL을 쓰고 URN을 잘 안 쓰는 이유?

위치는 변할 수 있지만, 이름은 변하지 않는다.

 

또, URN는 이름만으로 실제 리소스를 찾을 수 있는 방법이 보편화되지 않았다.

 

URL 분석

url예제 :  https://www.google.com/search?q=hello&hl=ko

 

https = 프로토콜 : 어떤 방식으로 자원에 접근할 것인가 하는 약속 규칙

ex) http, https, ftp

 

www.google.com = 호스트명 : 도메인명이나 IP주소 사용

 

/search = path : 리소스 경로, 계층적 구조

 

?q=hello = query : (key : value) 형태로 되어있다. 

Query parameter, query string이라고 불린다.

 

#fragment = http안에서 사용한다. 서버로 가지는 않음.


웹브라우저 요청 흐름

 

https://www.google.com/search?q=hello&hl=ko

참고사항 https는 433 포트가 생략되어있다.

DNS 조회 www.google.com/

PORT 조회 433포트

http 요청 메시지 생성 전달

서버에서 응답받은 메시지로 페이지 구성

 


위에 동작을 글로 표현한다면? 

클라이언트

 

소켓 라이브러리로 

구글 서버와 연결

TCP/IP로 포장 후 전송 데이터(http메시지) 전송

 

서버

http메시지 분석 후 동작

http 응답 메시지를 전송해준다.


http

HyperText Transfer Protocol

모든 것을 http으로 전송할 수 있다.

 

Http/1.1을 가장 많이 사용한다.

 

기반 프로토콜

http/1.1, http/2는 TCP 기반

http/3은 UDP 기반이다.

 

현재는 http/1.1이 많이 사용된다.

 

관리자 도구에서 프로토콜을 확인할 수 있다.

아래 사진은 http/3을 사용하는 걸 확인할 수 있다.


HTTP 특징

1. 클라이언트 서버 구조

2. 무상태 프로토콜 (스테이스 리스), 비연결성

HTTP 메시지

단순함, 확장 가능

 

1. 클라이언트 서버 구조

 

Request Response 구조

 

클라이언트는 서버에 요청을 보내고, 응답을 대기

서버가 요청에 대한 결과를 만들어서 응답

 

클라이언트 -> 요청 -> 서버

클라이언트 <- 응답 <- 서버

 

클라이언트는 ui를 그리는데 집중한다.

비즈니스 로직은 서버에 다 처리한다.

 

대용량 트래픽 처리에 유리하다

백앤드에서 아키텍처를 정해 비즈니스 로직을 처리한다.

 

2. 무상태 프로토콜

 

서버가 클라이언트의 상태를 보존 X

장점 : 서버 확정성 높음(스케일 아웃)

단점 : 클라이언트가 추가 데이터 전송

 

Stateful 상태 유지

무상태의 반대!

서버가 이전 상태를 보존한다.

같은 서버로 항상 유지되어야 한다.

또 사용 서버가 장애 날 경우 처음부터 다시 시작해야 한다.

 

Stateless 무상태

프런트에서 데이터를 저장하고 있고

서버에 전달한다.

 

상태 유지는 서버가 바뀔 경우 장애가 난다.

무상태는 서버가 바뀌어도 결제할 수 있다.

 

즉, 필요한 데이터를 그때그때 모두 주기에

 서버를 대량 증설해도 문제없다.

 

Stateless 한계 및 단점

모든 것을 무상태로 설계할 수 없다.

http데이터를 많이 보내야한다.

HTTP

 

인터넷 네트워크

 

인터넷 통신이라고 함은? 클라이언트와 서버가 통신하는 것을 말한다..

 

클라이언트 <- 인터넷 -> 서버

 

중간 노드(서버)들을 거쳐서 데이터가 전달된다.

 

IP(인터넷 프로토콜)

데이터를 전달하려면 주소가 필요한데 그것이 IP이다.

 

IP주소를 통해 데이터를 전달한다.

패킷단위로 전송한다.

 

패킷에 출발지IP,목적지IP등이 저장된다.

클라이언트 -> 서버, 서버 -> 클라이언트

양쪽 다 모두 동일하다.


IP만 있을 때 문제점!

1. 비연결성

상대 주소가 있는 없든 보낸다.

대상 서버가 패킷을 받을 수 있는지 모른다.

 

2. 비신뢰성

패킷 전송 중 패킷이 사라질 때?

패킷의 용량이 클 때 패킷이 순서대로 안 갈 수 있다.

 

3. 프로그램 구분

여러 개 프로그램 구동


이 문제를 해결하기 위해

TCP\UDP를 사용된다.

애플리케이션 계층  HTTP,FTP
전송 계층  TCP,UDP
인터넷 계층 IP
네트워크 인터페이스 계층  

4단계 형식으로 감싸 진다 

 

즉, IP 위에 TCP를 이용해 사용할 수 있게 한다고

생각하면 된다.

 

4단계 예제

ex) 채팅

socket(애플리케이션 계층)

 (socket(애플리케이션계층),TCP)

((socket(애플리케이션 계층), TCP), IP)

 

식으로 감싸서 전달한다.

 

TCP를 통해 IP에서 부족한 것을 채워준다.

 

IP : 출발지 IP, 목적지 IP

TCP : 출발지 PORT, 목적지 PORT, 전송 제어, 순서, 검증 정보

 

TCP 사용 이유

연결 지향 - 먼저 연결해보고 메시지를 보낸다.

물리적으로 연결된 게 아니라

논리적으로 연결되어 있다.

TCP 3 way handshake

  1. SYN (클라이언트-> 서버)
  2. SYN + ACK (서버 -> 클라이언트)
  3. ACK (클라이언트 -> 서버) 데이터도 함께 전송 가능

 

데이터 전달 보증 - 메시지 누락을 체크할 수 있다.

순서 보장

 

UDP - 기능이 거의 없다.

IP와 동일하지만 PORT가 추가되었다.

장점 속도가 빠르다.

 

최근엔 UDP를 많이 사용하려 한다.


PORT

 

PORT는 애플리케이션 하나에 하나씩 할당된다.

한 번에 둘 이상을 연결하기 위해 사용한다.

IP만 있다고 생각하면 패킷들을 분리할 방법이 없다.

 

IP는 서버를 찾고 PORT는 애플리케이션을 구분한다.


DNS(Domain Name System)

 

IP는 기억하기 어렵다.

또 IP는 변경될 수 있다.

 

도메인명으로 IP주소를 연결한다.

도메인명은 그대로있고 변경된 IP주소를 연결하면된다.

스프링 이전에 Servlet을 직접 사용할땐 URL마다 하나하나 만들어야하고

 

각각의 설정하기가 매우 복잡했다.

 

이것을 해결하는것이 Spring의 dispatcherServlet이다.

매번 반복작업하던 비생산적인 작업들을 스프링이 뒷단에서 직업해주고 있다.

 

HandlerMapping 으로 클라이언트가 서버에 요청할때 접근할 수 있게 해주고

HandlerAdapter로 요청에 맞는 기능을 찾아준다.

//스프링 내부 소스코드
//스프링이 구동될떄 초기에 여러 형태를 미리 준비해놓을 수 있다.

protected void initStrategies(ApplicationContext context) {
    this.initMultipartResolver(context);
    this.initLocaleResolver(context);
    this.initThemeResolver(context);
    this.initHandlerMappings(context);
    this.initHandlerAdapters(context);
    this.initHandlerExceptionResolvers(context);
    this.initRequestToViewNameTranslator(context);
    this.initViewResolvers(context);
    this.initFlashMapManager(context);
}

또 한 여러 작업을 하고 클라이언트에게 다시 송출할때도

viewResolver로 보내준다.

viewResolver는 기본으로 제공해주지만 아래처럼 커스텀해서 사용할 수 있다.

//viewResolver 설정해서 간단하게 사용하기

@Configuration
@ComponentScan
public class OneServletWebConfig {

    @Bean
    public ViewResolver viewResolver() {
        InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
        viewResolver.setPrefix("/WEB-INF/");
        viewResolver.setSuffix(".jsp");
        return viewResolver;
    }
}

 

요청을 받고 요청의 맞는 기능을 구현하고 형태에 맞게 화면에 전송하는 것을 다구현하지않고

스프링의  dispatcherServlet이 구현해준다.

TDD 테스트 주도 개발이란?

 

개발을 먼저하지 않고 테스트코드를 먼저 만든다.

시간이 많이 걸리는 단점은 있지만 프로젝트를 유지보수할때 큰효과가 나타난다.

 

mocha,should는 단위테스트이다.

Ex)함수테스트 : 함수별로 테스트한다.

 

mocha 모카란?

테스트에 사용하는 라이브러리

테스트 코드를 작성하면 테스트를 실행시켜주는것

 

테스트수트 : 테스트 환경을 꾸며준다. describe()를 사용한다.

 

테스트케이스 : 실제 테스트를 말하며 모카에서는 it()을 구현한다.

 

 

설치방법 : Npm install mocha —saver-dev

-dev : -dev를 하면 개발환경에서 필요하다는 말이다.

 

테스트 실행방법

node_modules/.bin/mocha [테스트파일명] 를 실행한다.

 

테스트 코드

const utils = require('./testUtils.js');
const assert = require('assert');

//mocha는 node로 실행할 수 없다.
describe('utils.js모듈의 capitialize 함수', ()=>{
    it('문자열 첫번째 문자를 대문자로 변환한다.', () =>{
        //테스트 코드 작성
        const result = utils.capitialize('hello');
        assert.equal(result, 'Hello');
    })

    it('문자열 첫번째 문자를 소문자로 변환한다.', () =>{
        //테스트 코드 작성
        const result = utils.capitialize('hello');
        assert.equal(result, 'hello');
    })
})

 

 

 

Should.js

테스트에 사용하는 라이브러리

슈드는 검증 라이브러리 이다.

예제 목록:https://github.com/tj/should.js/  

 

설치하기: npm I should —save-dev

테스트 코드

const utils = require('./testUtils.js');
const should = require('should');

//mocha는 node로 실행할 수 없다.
describe('utils.js모듈의 capitialize 함수', ()=>{
    it('문자열 첫번째 문자를 대문자로 변환한다.', () =>{
        //테스트 코드 작성
        const result = utils.capitialize('hello');
        result.should.be.equal('Hello');

    })

    it('문자열 첫번째 문자를 소문자로 변환한다.', () =>{
        //테스트 코드 작성
        const result = utils.capitialize('hello');
        result.should.be.equal(result, 'hello');
    })
})

 

슈퍼테스트

통합테스트에 사용한다.

Ex)api의 기능테스트

내부적으로 express를 구동시키고 테스트코드에서 작성된 시나리오대로 결과를 검증한다.

 

예제:https://github.com/visionmedia/supertest

 

설치하기:npm I supertest —save-dev

 

const request = require('supertest');

const express = require('express');



const app = express();



app.get('/user', function(req, res) {

  res.status(200).json({ name: 'john' });

});



request(app)

  .get('/user')

  .expect('Content-Type', /json/)

  .expect('Content-Length', '15')

  .expect(200)

  .end(function(err, res) {

    if (err) throw err;

  });

 

예제처럼 API의결과를 시나리오대로 테스트해보는것이다.

그럼 Servlet이란? 

자바엔터프라이즈 에디션에서 제공하는 웹을 만들 수 있게 제공하는것이다.

요청당 쓰레드를 사용한다.

 

지금은 직접 사용하진않지만 Spring의 기초뼈대이다.

 

서블릿은 단독으로 실행할 수 없고 서블릿 컨테이너가 실행해야한다.

 

서블릿 스펙을 준수하는 컨테이너(톰캣)가 서블릿의 라이플사이클을 이용해 실행한다.

 

라이플사이클

서블릿컨테이너가 서블릿 인스턴스의 init메소드를 호출하여 초기화한다.

 

서블릿 컨테이너가 구동될때 서블릿 리스너가 DB에 연결하고 여러가지 서블릿에 제공할 수 있다.

서블릿컨텍스트에서 꺼내서 사용한다.

 

서블릿 컨테이너가 종료할때 서블릿 리스너를 이용해 DB커넥션을 반납한다.

 

 

Java servlet API은 런타임때 빠진다. 컨테이너가 가지고있다.

 

Servlet 예제

public class HelloServlet extends HttpServlet {

    //원시적인 ServletApplication
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("doGet");
        ApplicationContext applicationContext = (ApplicationContext) getServletContext().getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);

        HelloService helloService = applicationContext.getBean(HelloService.class);
        resp.getWriter().println("Hello "+ helloService.getName());
    }

    private Object getName() {
        return getServletContext().getAttribute("name");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doPost(req, resp);
    }

    @Override
    public void init() throws ServletException {
        System.out.println("init");
    }

    @Override
    public void destroy() {
        System.out.println("destroy");
    }
}

 

서블릿에서 사용하는 두가지 기능

 

서블릿리스너

servletlistener

public class MyListener implements ServletContextListener {
    public void contextInitialized(ServletContextEvent sce) {
        System.out.println("contextInitialized");
        //서블릿보다 윗개념이기에 서블릿에서 꺼내서 사용할 수 있다.
        sce.getServletContext().setAttribute("name","사용");
    }

    public void contextDestroyed(ServletContextEvent sce) {
        System.out.println("contextDestroyed");
    }
}

-서블릿 컨테이너보다 윗개념

-서블릿컨테이너에서 발생하는 이벤트에 특정한 코드를 실행할때 사용한다.

-종료될때도 사용할 수 있다.

-(DB연결등 여러가지)

 

서블릿필터

servletfilter

public class MyFilter implements Filter {
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("Filter");
        //체인형식으로 다음필터에게 전달해주어야한다.
        filterChain.doFilter(servletRequest, servletResponse);
    }

    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("My Filter init");
    }

    public void destroy() {
        System.out.println("My Filter destroy");
    }
}

-필터를 거쳐 서블릿에간다

-요청이 들어왔을때 서블릿으로 가기전에 전처리할때 사용한다.

-여러개의 서블리셍 추가적인 작업을 하거나 특정한 URL에 적용할 수 있다.

-체인 구조로 순차적으로 실행된다.

설치방법

npm install express --save 로 설치

 

express의 사용이유

어플리케이션

- 익스프레스 객체를 어플리케이션이라고 한다.

- 서버에 필요한 미들웨어를 추가한다.

- 라우팅 설정을 할 수 있다.

- 서버를 요청 대기상태로 한다.

미들웨어

- 서버에기능을 추가하고 싶을때 사용하는 함수들의 배열이다.

 

라우팅

- 여러가지 URL로 들어올때 응답을 설정한다.

 

요청객체

- request로 생각하면된다. request를 한번더 감싼다.

응답객체

- response로 생각하면된다. response를 한번더 감싼다.

 

use로 미들웨어를 사용한다.

 

//미들웨어 만들기

function logger(req, res, next){

    console.log("Test");

    next();

}



app.use(logger);

 

next(); 를 선언해야 다음 미들웨어도 동작한다.

 

express실행하기

Port번호를 설정하고 실행해야한다.

var express = require('express');
var app = express();
//중간 로직
app.listen('3000', function (){
    console.log('apiServerStart');
})

Environment

 

Application Context은 Bean Factory기능만 하는게 아니다.

Application Context의 기능은 여러가지가 있다.

 

그 중하나가 Environment이다.(환경설정)

 

Environment 프로파일(Profile) 

프로파일이란 Bean들의 묶음 특정 Bean들을 묶어 사용할때 유용하다.

 

@Profile로 ComponentScan으로 등록할 수 있고 

@Repository

@Profile("!prod")//ComponentScan에 등록하기

public class TestBookRepository implements BookRepository{



    @PostConstruct

    @Override

    public void run() {

        System.out.println("TestBookRepository 생성");

    }

}

 

@Configuration을 통해 Class형식으로 등록할 수있다 

//profile정하고 class로 등록하는방법

@Configuration

@Profile("test")//test profile일때만 사용

public class TestConfiguration {



    @Bean

    public BookRepository bookRepository(){

        return new TestBookRepository();

    }

}

또 특정 profile만 실행하는 것이 아닌 특정 프로파일이 아닐때도 사용가능하다. !not 사용

prod Profile이 아닐때 예제코드

@Repository
@Profile("!prod")//ComponentScan에 등록하기
public class TestBookRepository implements BookRepository{

    @PostConstruct
    @Override
    public void run() {
        System.out.println("TestBookRepository 생성");
    }
}

 

environment를 통해 properties 사용하기

Configuraion에 VMoption을 등록하거나

resources 경로 아래 Properties 파일을 생성하고

스프링시작부분에 @PropertySource("classpath:./App.Properties”)를 등록하고

꺼내올 수 있다.

@SpringBootApplication

@PropertySource("classpath:./App.Properties")

public class EnvironApplication {



    public static void main(String[] args) {

        SpringApplication.run(EnvironApplication.class, args);

    }

}

 

 

BeanFactory에 등록된 bean들은 scope가 있다.

 

기본 Scope : Singleton Scope

Singleton Scope 이란?

해당 인스턴스가 하나뿐이다. 같은 인스턴스를 재사용하는 것이다.

 

기본적으론 생성된 bean은 SingletonScope이라 같은 인스턴스을 바라본다.

@Scope("prototype”)를 설정하면 매번 다른 인스턴스를 생성할 수 있다.

 

@Scope(“prototype”)차이점 보기

1.Single Class는 기본 Singleton 스코프

2.Proto Class는 prototype스코프로 선언

 

출력해보면 결과가 다르게 나온다.

@Component

public class Single {



    @Autowired

    Proto proto;



    public Proto getProto() {

        return proto;

    }

}



@Component @Scope("prototype")

public class Proto {

}



@Override

public void run(ApplicationArguments args) throws Exception {

        System.out.println("single");
        System.out.println(context.getBean(Single.class));
        System.out.println(context.getBean("single"));
        System.out.println(single);

        System.out.println("poroto");
        System.out.println(context.getBean(Proto.class));
        System.out.println("----");
        System.out.println(context.getBean("proto"));
        System.out.println("---");
        System.out.println("single.getProto().getClass() : 1----");
        System.out.println(context.getBean(single.getProto().getClass()));
        System.out.println("single.getProto().getClass() : 2----");
        System.out.println(context.getBean(single.getProto().getClass()));
        System.out.println("----같은 인스턴스 보는 이슈");
        System.out.println(context.getBean(Single.class).getProto());
        System.out.println(context.getBean(Single.class).getProto());


}
single
com.choi.scope.Single@4b6e1c0
com.choi.scope.Single@4b6e1c0
com.choi.scope.Single@4b6e1c0
poroto
Proto 인스턴스 생성
com.choi.scope.Proto@26cb5207
----
Proto 인스턴스 생성
com.choi.scope.Proto@15400fff
---
single.getProto().getClass() : 1----
Proto 인스턴스 생성
com.choi.scope.Proto@18d910b3
single.getProto().getClass() : 2----
Proto 인스턴스 생성
com.choi.scope.Proto@1e7ab390
----같은 인스턴스 보는 이슈
com.choi.scope.Proto@11d4dbd6
com.choi.scope.Proto@11d4dbd6

ProtoType에서 Singleton을 Autowired 하면 큰 문제없다.

하지만 Singleton에서 Prototype을 Autowried 한다면?

계속 같은 주소를 불러오는 이슈가 있다.

이때는 @Scope(value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)

설정으로 프록시로 감싸서 사용하면 된다.

 

@Component @Scope(value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)

public class Proto {



    @PostConstruct

    public void main(){

        System.out.println("Test");

        System.out.println("생성자 주입");

        System.out.println("Proto 인스턴스 생성");

    }

}
single
com.choi.scope.Single@2f897dab
com.choi.scope.Single@2f897dab
com.choi.scope.Single@2f897dab
poroto
Proto 인스턴스 생성
com.choi.scope.Proto@39a87e72
----
Proto 인스턴스 생성
com.choi.scope.Proto@5d2828c9
---
single.getProto().getClass() : 1----
Proto 인스턴스 생성
com.choi.scope.Proto@3a082ff4
single.getProto().getClass() : 2----
Proto 인스턴스 생성
com.choi.scope.Proto@45acdd11
----같은 인스턴스 보는 이슈 해결
Proto 인스턴스 생성
com.choi.scope.Proto@3f0d6038
Proto 인스턴스 생성
com.choi.scope.Proto@237f7970

DB 연결을 자주 수정해야 할 때가 있다면 유용하게 사용할 수 있을 것 같다.

 

보통 Singletion 생명주기가 길다. 여러 스레드에서 참조하는 경우 값이 계속바뀔수 있기에 prototype으로 생성하는것이 좋을거 같다.

Autowired란?

빈에서 다른 빈의 의존성을 주입하는것이다.

즉, A bean이 B bean을 사용하려면 @Autowired해준다.

 

Autowired된 bean을 먼저 생성한다. 생각해보면 먼저 생성되어있어야 사용할 수 있다.

@Component
public class BookServiceRunner implements ApplicationRunner {

    //BookService를 사용한다.
    @Autowired
    BookService bookService;

    public void run(ApplicationArguments args) throws Exception{
        bookService.printBookRepository();
    }
}

 

기본적으로는 Autowired를 사용하면 의존성이 있어야하므로 bean으로 등록되어있어야한다.

 

필드,setter에선 (required = false) 으로 의존성을 강제하지 않을 수 있다.

필드에 사용하는것과 생성자에 사용하는건 약간다르다.

    //BookService를 사용한다.
    //의존성 강제 설정해체
    @Autowired (required = false)
    BookService bookService;

 

또 repository가 여러개일 경우 충돌이 난다. 

이럴땐 @Primary나 @Qualifier("huBookRepository")로 강제해주어야한다.

Primary형식

//Primary형식
@Repository @Primary
public class MyBookRepository implements BookRepository {
    @Override
    @PostConstruct
    public void setUp() {
        System.out.println("MyBookRepository");
    }
}

Qualifier형식

    //Qualifier형식
    @Autowired @Qualifier("myBookRepository")
    BookRepository bookRepository;

아니면 모두 사용하고 싶을 경우 LIst형식으로 받을 수 있다.

    @Autowired
    List<BookRepository> bookRepository;

LOG를 찍어보게되면 BookRepository를 implements한 클래스가 모두 나온다

@Service
public class BookService {

    @Autowired
    List<BookRepository> bookRepository;

    public void printBookRepository(){
        this.bookRepository.forEach(System.out::println);
        System.out.println(bookRepository.getClass());
    }
}

인스턴스 생성될때 동작하기 

@Repository
public class MyBookRepository extends TestExtends implements BookRepository  {
    @Override
    @PostConstruct
    public void setUp() {
        System.out.println("MyBookRepository");
    }
}

 

 

 

 

@Component와 컴포넌트 스캔

Component는 스프링 3.1부터 적용되었다.

 

@Component를 사용할 경우 IOC컨테이너에 bean으로 등록한다.

@Service,@Repository,@Controller등 여러가지 애노테이션엔

@Component가 포함되어있다.

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component //Service에 등록되어있는 Component
public @interface Service {
    @AliasFor(
        annotation = Component.class
    )
    String value() default "";
}

ComponentScan

여러가지 bean들을 검색하고 IOC컨테이너에 적재한다.

 

스프링부트에선 @SpringBootApplication가 시작지점이 된다.

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

}

SpringBootApplication 내부 모습

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
    excludeFilters = {@Filter(
    type = FilterType.CUSTOM,
    classes = {TypeExcludeFilter.class}
), @Filter(
    type = FilterType.CUSTOM,
    classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {

 

@SpringBootApplication 이하의 모든 클래스가 @ComponentScan

(basePackages,basePackageClasses)에 의해 컴포넌트 스캔이된다.

 

만약 개발할때 bean주입이 안되면 컴포넌트 스캔위치를 살펴봐야한다.

모든걸 bean으로 등록하는것이 아니라 필터로 걸러낼 수 있다.

 

, 빈을 다등록하는게 부담된다면 펑션으로 특정빈만 등록할 있다.

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

Spring Environment 기초 사용방법  (0) 2020.07.13
Spring Scope란?  (0) 2020.07.13
AOP란? AOP 기초개념  (0) 2020.07.09
Spring IoC(Inversion of Control),Bean,의존성주입이란?  (0) 2020.07.08
프레임워크 개념  (0) 2018.01.26

AOP

Aspect(관심사)

공통된 관심사?로 이해하면 쉽다.

 

여러 클래스에서 같은 로직(관심사)이 실행될 때 사용한다.

 

공통적인 로직을 클래스에 하나하나 추가하지 않고 별도의 클래스로 생성한다.

 

예를 들어 모든 로직의 수행시간을 측정하려고 한다.

//시작

StopWatch sw = new StopWatch();

sw.start();

//여러가지 로직들

//끝

sw.stop();

System.out.println(sw.prettyPrint());

 

공통된 로직이 모두 들어간다.

 

공통된 로직을 분리 후 실행하는방법

 

-컴파일

컴파일할때 코드를 넣어준다.

 

-바이트코드 조작

A.java -> A.Class 하고 실행 시 클래스 로더가 메모리에 올릴 때 조작한다.

 

컴파일, 바이트코드 조작의 차이점 코드가 아닌 메모리에 올라간다.

두 개다 AspectJ가 지원한다.

 

-프록시패턴(스프링AOP)

인터페이스 implements를 통해 구현한다. 

한번 더 감싸는 구조로 생각하면 쉽다.

 

(

공통 로직

(실행 로직)

공통 로직

)

자동으로 bean 등록될 때 만들어진다.

 

 

애노테이션을 사용하여 AOP 적용한 예제

@LogExecutionTime을 만들어보기

@GetMapping("/owners/find")

//만들 애노테이션 명

@LogExecutionTime

public String initFindForm(Map<String, Object> model) {

model.put("owner", new Owner());

return "owners/findOwners";

}

 

구현할 애노테이션 생성

@Target(ElementType.METHOD)

@Retention(RetentionPolicy.RUNTIME)

public @interface LogExecutionTime {

}

 

 

@Around로 ProceedingJoinPoint를 파라미터로 받는다.

ProceedingJoinPoint는 애노테이션을 설정한 타깃을 말한다.

즉 애노테이션 달려있는 메서드!

//bean으로 등록

@Component

//Aspect 설정

@Aspect

public class LogAspect {



Logger logger = LoggerFactory.getLogger(LogAspect.class);



@Around("@annotation(LogExecutionTime)")

public Object LogExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable{

StopWatch sw =new StopWatch();

sw.start();

//위애 애노테이션 달려있는 메소드 실행

Object procced= joinPoint.proceed();



sw.stop();

logger.info(sw.prettyPrint());



return procced;

}

}

 

같은 로직인데 여러 군데 들어간다면 AOP를 활용하는 게 좋다.

애노테이션을 쓰지 않는 방법도 있다.

여러 가지방법과 사용방법이 존재한다.

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

Spring Scope란?  (0) 2020.07.13
Spring Autowired와 ComponentScan이란  (0) 2020.07.10
Spring IoC(Inversion of Control),Bean,의존성주입이란?  (0) 2020.07.08
프레임워크 개념  (0) 2018.01.26
SpringMVC란?  (0) 2018.01.11

+ Recent posts