인터넷에서 컴퓨터 둘은 어떻게 통신할까?

 

복잡한 인터넷 망에서 클라이언트와 서버는 어떻게 통신하는 걸까?

이를 이해하기 위해서는 IP(인터넷 프로토콜)에 대해 알아야 한다.

 

IP(인터넷 프로토콜)의 역할

  • 지정한 IP 주소(IP Address)에 데이터 전달
  • 패킷(Packet)이라는 통신 단위로 데이터 전달

IP 패킷은 전송 데이터, 출발지 IP, 목적지 IP, 기타 정보 등을 담고 있다.

클라이언트와 서버는 각각 출발지 IP와 목적지 IP에 패킷을 전송하면서 통신한다.

하지만 IP 프로토콜에는 많은 단점과 한계점이 존재한다.

 

IP 프로토콜의 한계

  • 비연결성
    - 대상 서버가 패킷을 받을 수 있는 상태인지 알 수 없다.
    - 패킷을 받을 대상이 없거나 서비스 불능 상태여도 패킷을 전송한다.
  • 비신뢰성
    - 중간에 패킷이 사라져서 손실이 발생할 수 있다.
    - 패킷이 순서대로 오지 않을 수 있다.
  • 프로그램 구분
    - 같은 IP를 사용하는 서버에서 통신하는 애플리케이션이 둘 이상이라면?
       IP만으로는 어떤 애플리케이션에 요청을 한 건지 전혀 알 수 없다는 문제가 발생한다.

이러한 문제점을 해결해 줄 수 있는 게 바로 전송 제어 프로토콜(TCP; Transmission Control Protocol)이다.

 

인터넷 프로토콜 스택의 4 계층

인터넷 프로토콜 스택 4 계층이란 인터넷에서 컴퓨터들이 서로 정보를 주고받는 데 쓰이는 프로토콜의 모음이다.

용도에 따라 4개의 계층으로 나뉘어있다.

애플리케이션 계층은 프로그램 간 통신을 위한 계층이다.

전송 계층은 송신자와 수신자를 연결하는 서비스에 대한 정보를 담는 계층이다.

인터넷 계층은 패킷을 목적지로 전송하기 위한 정보를 담는 계층이다.

네트워크 인터페이스 계층은 LAN 드라이버, LAN 장비 등 물리적 전송을 위한 계층이다.

 

실제 통신 예시는 다음과 같은 과정이 이루어진다.

TCP는 데이터를 세그먼트 단위로 나눠서 전송한다. 세그먼트는 패킷의 일부로서 데이터를 포함하며, TCP 헤더와 함께 전송된다.

 

MSS (Maximum Segment Size): TCP는 MSS라는 최대 세그먼트 크기를 사용하여 데이터를 나눈다.
이는 통신하는 양쪽의 호스트 간에 협상되며, 일반적으로 MTU (Maximum Transmission Unit) 크기에 맞추어 설정된다.

패킷 분할 (Packet Fragmentation): TCP는 패킷을 세그먼트로 나누어 전송하지만, 네트워크에서는 패킷 크기에 제한이 있을 수 있다.
이 경우, 패킷 분할이 발생할 수 있다. 라우터나 중간 장비에서 패킷이 네트워크 MTU보다 크다면, 패킷은 작은 조각으로 나눠져서 전송되며, 이를 패킷 분할이라고 한다.

TCP(전송 제어 프로토콜)의 특징

  • 연결지향 - TCP 3 Way handshake (가상 연결)
  • 데이터 전달 보증
  • 순서 보장
  • 신뢰성 있는 프로토콜

TCP는 다음과 같은 단계로 연결을 수행한다.

계속 연결되어 있는 상태는 아니고, 해당 연결 후 데이터를 받은 후에는 연결을 끊는다.

 

사용자 데이터그램 프로토콜(UDP; User Datagram Protocol)

UDP는 TCP와 반대로 기능이 거의 없다. TCP처럼 연결 지향적이지도 않고, 데이터의 전달이나 순서도 보장할 수 없다.

그렇지만 단순하고 빠르다는 장점이 있다.

IP(인터넷 프로토콜)과 유사하지만 포트와 체크섬(checksum) 정도가 추가된다.

기존에는 실시간 비디오 스트리밍이나 사용자가 직접 프로토콜을 제어하고자 할 때 많이 사용했다.
요새는 스트리밍이더라도 TCP를 사용하는 경우도 많다고 한다.
참고로 최근에 발표된 HTTP/3의 경우 UDP 기반의 QUIC 프로토콜을 사용한다.

 

PORT (포트)

포트는 같은 IP 내에서 프로세스를 구분하기 위한 역할을 한다. TCPUDP는 둘 다 포트를 사용하는 프로토콜이다.
각 포트는 번호로 구별되며, 이를 포트 번호라고 한다. 포트 번호 중에서 자주 쓰이는 포트를 well-known Port라고 한다.

 

포트 번호는 0 ~ 65535까지 할당 가능하며, 0 ~ 1023 포트 번호는 위에서 말한 well-known Port에 해당하여 사용하지 않는 편이 좋다.

대표적인 well-known port는 다음과 같다.

  • 20,21 - FTP
  • 23 - TELNET
  • 22 - SSH
  • HTTP - 80
  • HTTPS - 443

 

도메인 네임 시스템(DNS; Domain Name System)

우리는 클라이언트와 서버가 IP를 통해서 인터넷 망에서 통신한다고 배웠다.
하지만 IP는 너무 기억하기 어렵다는 단점이 있다. 또한 IP는 고정이 아니다 바뀔 수 있다.

이러한 문제점을 해결해 줄 수 있는 게 도메인 네임 시스템(DNS)이다.

 

DNS는 도메인 명을 IP 주소로 변환한다.

예를 들어 우리가 구글에 접속한다면 다음과 같은 과정이 이루어진다.

1. 클라이언트는 구글의 도메인명을 브라우저에 입력한다.

2. DNS 서버에서 구글의 도메인 명에 대한 서버의 실제 IP 주소로 변환해 준다.

3. 클라이언트는 서버 IP 주소로 통신한다.

 

DNS를 사용하면 위에서 말한 IP의 단점인 기억하기 어려운 점과 IP가 유동적으로 바뀌는 문제를 해결할 수 있다.

 

Reference 

https://www.inflearn.com/course/http-%EC%9B%B9-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC/dashboard

 

모든 개발자를 위한 HTTP 웹 기본 지식 - 인프런 | 강의

실무에 꼭 필요한 HTTP 핵심 기능과 올바른 HTTP API 설계 방법을 학습합니다., [사진] 📣 확인해주세요!본 강의는 자바 스프링 완전 정복 시리즈의 세 번째 강의입니다. 우아한형제들 최연소 기술

www.inflearn.com

 

'Computer Science > Network' 카테고리의 다른 글

HTTP의 특징  (0) 2023.09.19
URI 와 웹 브라우저 요청 흐름  (0) 2023.09.19
로드 밸런싱(Load Balancing)  (0) 2023.08.29
SSL/TLS HandShake  (0) 2023.08.29
HTTP와 HTTPS  (0) 2023.08.28

빈 스코프란?

앞서서

2023.09.08 - [Back-End/Spring] - 빈 생명주기 콜백에서 스프링 빈은 기본적으로 다음과 같은 이벤트 라이프 사이클을 갖는다고 했다.
스프링 컨테이너 생성 -> 스프링 빈 생성 -> 의존관계 주입 -> 초기화 콜백 -> 사용 -> 소멸 전 콜백 -> 스프링 종료

하지만 이는 스프링 빈이 기본적으로 싱글톤 스코프로 생성되기 때문이다.

스코프는 빈이 존재할 수 있는 범위를 뜻하며, 스프링은 싱글톤 빈 외에도 다양한 스코프를 지원한다.

 

  • 싱글톤 스코프
    - 기본 스코프, 스프링 컨테이너의 시작과 종료까지 유지되는 가장 넓은 범위의 스코프
  • 프로토타입 스코프
    - 스프링 컨테이너는 빈의 생성과 의존관계 주입만 관여하고, 더는 관리하지 않는 매우 짧은 스코프
  • 웹 스코프

먼저 싱글톤 스코프로 싱글톤 빈을 생성하여 테스트해보자.

싱글톤 스코프는 다음과 같은 문법을 사용하면 된다.(생략 시에도 기본적으로 스프링 컨테이너는 싱글톤 빈으로 생성한다)

@Scope("singleton")
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Scope;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import static org.assertj.core.api.Assertions.assertThat;

public class SingletonTest {
    @Test
    public void singletonBeanFind(){
        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(SingletonBean.class);
        SingletonBean singletonBean = ac.getBean(SingletonBean.class);
        SingletonBean singletonBean2 = ac.getBean(SingletonBean.class);
        System.out.println("singletonBean = " + singletonBean);
        System.out.println("singletonBean2 = " + singletonBean2);
        assertThat(singletonBean).isSameAs(singletonBean2);
    }

    @Scope("singleton")
    static class SingletonBean{
        @PostConstruct
        public void init(){
            System.out.println("SingletonBean init");
        }
        @PreDestroy
        public void destroy(){
            System.out.println("SingletonBean destroy");
        }
    }
}

테스트를 실행해 보자.

테스트 실행 시 빈이 같은 인스턴스임을 알 수 있으며,
빈 생성 뒤, 초기화하는 @PostConstruct과 빈 소멸 직전에 실행하는 @PreDestroy 또한 잘 실행된 걸 확인할 수 있다.

 

다음은 프로토타입 스코프로 빈을 생성해 보자.

import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Scope;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import static org.assertj.core.api.Assertions.assertThat;

public class PrototypeTest {
    @Test
    public void prototypeBeanFindTest(){
        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(PrototypeBean.class);
        PrototypeBean prototypeBean = ac.getBean(PrototypeBean.class);
        PrototypeBean prototypeBean1 = ac.getBean(PrototypeBean.class);
        System.out.println("prototype = " + prototypeBean);
        System.out.println("prototype = " + prototypeBean1);
        assertThat(prototypeBean).isNotSameAs(prototypeBean1);

        ac.close();
    }

    @Scope("prototype")
    static class PrototypeBean {
        @PostConstruct
        public void init(){
            System.out.println("prototype init");
        }
        @PreDestroy
        public void destroy(){
            System.out.println("prototype destroy");
        }

    }
}

기존 싱글톤 스코프 방식에서 애노테이션만 수정하였다.

@Scope("prototype")

테스트를 실행해 보자

실행 결과를 보면 프로토타입 스코프의 빈은 스프링 컨테이너에서 빈을 조회할 때 생성되고, 초기화 메서드도 실행된다.

프로토 타입 빈을 2번 조회했으므로, 다른 스프링 빈이 2번 실행되고 초기화도 2번 실행된 걸 알 수 있다.

그리고 ac.close()를 통해 종료를 선언했음에도 스프링 종료 직전에 실행되는 @PreDestroy 메서드가 실행되지 않았다.

 

이를 통해 프로토타입 스코프 빈은 요청할 때마다 새로운 빈이 생성되며,
스프링 컨테이너가 빈의 생성, 의존관계 주입, 초기화까지만 관여하고 더는 관리하지 않는다는 사실을 알 수 있다.

따라서 프로토타입 스코프 빈은 종료 메서드 호출 또한 클라이언트가 직접 해야 한다.

 

다음은 웹 스코프이다.

  • request
    - HTTP 요청 하나가 들어오고 나갈 때까지 유지되는 스코프, 각각의 HTTP 요청마다 별도의 빈 인스턴스가 생성되고, 관리된다.
  • session
    - HTTP Session과 동일한 생명주기를 가지는 스코프
  • application
    - 웹의 서블릿 콘텍스트와 같은 범위로 유지되는 스코프
  • websocket
    -웹 소켓과 동일한 생명주기를 가지는 스코프

해당 글에서는 request 스코프로 테스트 코드를 작성할 것이다.

request 스코프는 여러 http 요청이 동시에 왔을 때, 어떤 요청이 남긴 지 구분하기 어려울 때 쓰면 좋은 스코프이다.


먼저 build.gradle에 추가한다.

implementation('org.springframework.boot:spring-boot-starter-web')

먼저 로그를 남기기 위한 클래스를 작성해 보자.

import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.util.UUID;

@Component
@Scope(value = "request")
public class MyLogger {

    private String uuid;
    private String requestUrl;

    public void setRequestUrl(String requestUrl) {
        this.requestUrl = requestUrl;
    }

    public void log(String message){
        System.out.println("[uuid" +uuid+ "]["+requestUrl+"]" + message);
    }
    @PostConstruct
    public void init(){
        uuid = UUID.randomUUID().toString();
        System.out.println(this);
    }
    @PreDestroy
    public void close(){
        System.out.println(this);
    }
}

@Scope(value = "request")를 사용해서 request 스코프로 지정했다.
이제 이 빈은 HTTP 요청 당 하나씩 생성되고, HTTP 요청이 끝나는 시점에 소멸된다.

이 빈이 생성되는 시점에 자동으로 @PostConstruct 초기화 메서드를 사용해서 uuid를 생성해서 저장해 둔다.

이 빈은 HTTP 요청 당 하나씩 생성되므로, uuid를 저장해 두면 다른 HTTP 요청과 구분할 수 있다.

이 빈이 소멸되는 시점에 @PreDestroy를 사용해서 종료 메시지를 남긴다.

requestURL은 이 빈이 생성되는 시점에는 알 수 없으므로, 외부에서 setter로 입력받는다.

 

테스트용 컨트롤러와 서비스를 작성한다.

import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;

@Controller
@RequiredArgsConstructor
public class LogDemoController {
    private final LogDemoService logDemoService;
    private final MyLogger myLogger;

    @RequestMapping("log-demo")
    @ResponseBody
    public String logDemo(HttpServletRequest request){
        String requestURL = request.getRequestURL().toString();

        System.out.println(myLogger.getClass());
        myLogger.setRequestUrl(requestURL);
        myLogger.log("controller");
        logDemoService.logic("testId");
        
        return "OK";

    }
}
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.stereotype.Service;

@Service
@RequiredArgsConstructor
public class LogDemoService {

    private final MyLogger myLogger;
    public void logic(String id){
        myLogger.log("service Id = " + id);
    }
}

실행 시 다음과 같이 에러가 발생한다.

Error creating bean with name 'myLogger': 
Scope 'request' is not active for the current thread; 
consider defining a scoped proxy for this bean 
if you intend to refer to it from a singleton;

싱글톤 빈은 스프링 애플리케이션을 실행 시에 빈을 생성하고 의존 관계를 주입한다.
하지만 request 스코프 빈은 위에서 http 요청이 들어와야 빈을 생성한다고 했다.
따라서 생성되지 않은 빈을 조회하려고 하여 에러가 발생했다.
이 문제를 해결하기 위해서는 객체를 조회할 때까지 빈 생성을 지연해야 한다.

해당 문제를 해결하기 위해서는 두 가지 방안이 있다.

하나는 ObjectProvider를 사용하는 방법이다.

import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;

@Controller
@RequiredArgsConstructor
public class LogDemoController {
    private final LogDemoService logDemoService;
    private final ObjectProvider<MyLogger> myLoggerProvider;

    @RequestMapping("log-demo")
    @ResponseBody
    public String logDemo(HttpServletRequest request){
        String requestURL = request.getRequestURL().toString();

        MyLogger myLogger = myLoggerProvider.getObject();
        System.out.println(myLogger.getClass());
        myLogger.setRequestUrl(requestURL);
        myLogger.log("controller");
        logDemoService.logic("testId");
        
        return "OK";

    }
}
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.stereotype.Service;

@Service
@RequiredArgsConstructor
public class LogDemoService {

    private final ObjectProvider<MyLogger> myLoggerProvider;
    public void logic(String id){
        MyLogger myLogger = myLoggerProvider.getObject();
        myLogger.log("service Id = " + id);
    }
}

ObjectProvider를 사용하면 빈의 생성을 myLoggerProvider.getObject()가 호출될 때까지 지연시킬 수 있다.

스프링 애플리케이션을 실행하면 정상적으로 실행되고, api를 호출해 보면 다음과 같은 결과를 얻을 수 있다.

 

한 가지 방법이 더 있는데 프락시 방식이다.

수정한 ObjectProvider를 원래대로 돌린 뒤 스코프 애노테이션에 해당 내용을 추가한다.

@Component
@Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class MyLogger {

만약 적용 대상이 클래스면

proxyMode = ScopedProxyMode.TARGET_CLASS

적용 대상이 인터페이스면 다음과 같이 선언하면 된다.

proxyMode = ScopedProxyMode.INTERFACES

다시 스프링 애플리케이션을 실행하고, api를 호출해 보자.

잘 동작하는 것을 확인할 수 있다.

System.out.println("myLogger = " + myLogger.getClass());

에서 출력된 첫 번째 줄을 보면 CGLIB라는 내용을 볼 수 있는데
프록시는 CGLIB라는 라이브러리로 내 클래스를 상속 받은 가짜 프록시 객체를 만들어서 주입한다.


@Scope의 proxyMode = ScopedProxyMode.TARGET_CLASS)를 설정하면

스프링 컨테이너는 CGLIB라는 바이트코드를 조작하는 라이브러리를 사용해서, MyLogger를 상속받은 가짜 프록시 객체를 생성한다.

가짜 프록시 객체는 요청이 오면 그때 내부에서 진짜 빈을 요청하는 위임 로직이 들어있다.
가짜 프록시 객체는 실제 request scope와는 관계가 없다. 그냥 가짜이고, 내부에 단순한 위임 로직만 있고, 싱글톤처럼 동작한다.

 

프록시 객체 덕분에 클라이언트는 마치 싱글톤 빈을 사용하듯이 편리하게 request scope를 사용할 수 있다.

하지만 싱글톤 패턴처럼 사용하는 것 같지만 실제로는 아니기 때문에 주의가 필요하다.
이러한 특별한 스코프는 꼭 필요한 곳에만 최소화하여 사용해야 유지보수 시에 어려움을 겪지 않는다.
또한 웹 스코프가 아니어도 프록시 객체는 사용할 수 있다.

 

'Back-End > Spring' 카테고리의 다른 글

서블릿(Servlet)과 Spring Boot  (0) 2023.10.17
빈 생명주기 콜백  (0) 2023.09.08
컴포넌트 스캔  (0) 2023.09.08
싱글톤 패턴과 싱글톤 컨테이너  (0) 2023.09.08
스프링 핵심 원리 -2  (0) 2023.09.01

데이터베이스 커넥션 풀이나, 네트워크 소켓처럼 애플리케이션 시작 시점에 필요한 연결을 미리 해두고, 

애플리케이션 종료 시점에 연결을 모두 종료하는 작업을 진행하려면, 객체의 초기화와 종료 작업이 필요하다.
스프링을 통해 이러한 초기화 작업과 종료 작업을 어떻게 진행하는지 예제로 알아보자.

 

스프링 빈은 다음과 같은 사이클을 가진다.

 

객체를 생성하고, 의존 관계를 주입한다.

 

스프링 빈은 의존 관계 주입이 끝나야 필요한 데이터를 사용할 수 있는 준비가 완료되는데

그렇다면 초기화 작업은 의존관계가 모두 주입된 후에 호출되어야 한다.


그런데 의존관계 주입이 끝난 걸 어떻게 알 수 있을까?

 

스프링은 의존관계 주입이 완료되면 스프링 빈에게 콜백 메서드를 통해 초기화 시점을 알려주는 다양한 기능을 제공하며,
스프링 컨테이너가 소멸 직전에 소멸 콜백을 준다.

 

스프링 빈의 이벤트 라이프 사이클은 다음과 같다.

스프링 컨테이너 생성 -> 스프링 빈 생성 -> 의존관계 주입 -> 초기화 콜백 -> 사용 -> 소멸 전 콜백 -> 스프링 종료

 

참고로 모든 빈이 이런 사이클을 따르지 않는다.
싱글톤 빈은 위의 라이프 사이클대로 진행되지만 웹 스코프프로토타입 스코프로 인해 생성되는 빈들은 짧은 생명 주기를 가진다.

 

스프링은 크게 3가지 방법으로 빈 생명주기 콜백을 지원한다.

 

  • 인터페이스(InitializingBean, DisposableBean) 
  • 설정 정보에 초기화 메서드, 종료 메서드 지정 
  • @PostConstruct, @PreDestroy 애노테이션 지원

여기서는 3번째 방법을 서술하려고 한다.

 

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

public class NetworkClient{
    private String url;

    public NetworkClient() {
        System.out.println("생성자 호출, url =  " + url);
    }

    public void setUrl(String url){
        this.url = url;
    }

    public void connect(){
        System.out.println("connect = " + url);
    }
    public void call(String message){
        System.out.println("call: "+url + " message = " + message);
    }

    public void disconnect(){
        System.out.println("close: " + url);
    }

    @PostConstruct
    public void init() throws Exception {
        connect();
        call("초기화 연결 메시지");
    }
    @PreDestroy
    public void close() throws Exception {
        disconnect();
    }

}

@PostConstruct 애노테이션은 초기화 작업을 수행하는 메서드에 사용되며, 해당 메서드는 빈이 생성된 후에 실행된다.
@PreDestroy 애노테이션은 빈 소멸 전에 작업을 수행하는 메서드에 사용 되며, 해당 메서드는 빈이 소멸되기 전에 실행된다.

 

해당 방식은 여러가지 장점이 있다.

우선 최신 스프링에서 제일 권장되는 방식이며, 애노테이션 하나만 붙이면 되므로 매우 편리하다.

또한 임포트된 걸 보면 스프링에 종속된 기술이 아닌 JSR-250이라는 자바 표준이다.

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

자바 표준이므로 스프링이 아닌 다른 컨테이너에서도 사용이 가능하다.

 

단점은 외부 라이브러리에는 적용하지 못한다는 점인데 코드를 고칠 수 없는 외부 라이브러리를 초기화나 종료해야 한다면

@Bean의 initMethod , destroyMethod를 사용하자.

'Back-End > Spring' 카테고리의 다른 글

서블릿(Servlet)과 Spring Boot  (0) 2023.10.17
빈 스코프  (0) 2023.09.08
컴포넌트 스캔  (0) 2023.09.08
싱글톤 패턴과 싱글톤 컨테이너  (0) 2023.09.08
스프링 핵심 원리 -2  (0) 2023.09.01

컴포넌트 스캔은 스프링에서 자동으로 빈을 등록하고 관리하기 위한 방법이다.

컴포넌트 스캔은 이름 그대로 @Component 애노테이션이 붙은 클래스를 스캔해서 스프링 빈으로 등록한다.

 

컴포넌트 스캔을 사용하려면 먼저 @ComponentScan을 설정 정보에 붙여주면 된다.

컴포넌트 스캔을 사용하면 @Configuration 이 붙은 설정 정보도 자동으로 등록되기 때문에
excludeFilters를 이용해서 설정정보는 컴포넌트 스캔 대상에서 제외했다.

@Configuration
@ComponentScan(
         excludeFilters = @Filter(type = FilterType.ANNOTATION, classes =
 Configuration.class))
public class AutoAppConfig {
}

컴포넌트 스캔이 만약 탐색 시에 모든 자바 클래스를 전부 탐색한다면 시간이 오래 걸릴 것이다.
컴포넌트 스캔은 탐색 위치를 지정할 수 있는데 기본적으로 @ComponentScan이 붙은 설정 정보 클래스가 시작 위치가 된다.

 

 @ComponentScan(
         basePackages = "hello.core",
}

다음과 같이 지정도 가능하다.

권장하는 방법은 패키지 지정을 하지 않고, 설정 정보 클래스의 위치를 최상단에 두는 것을 추천한다.
최근의 스프링 부트 또한 이 방법을 기본적으로 제공한다.

 

컴포넌트 스캔의 대상

컴포넌트 스캔은 @Component 뿐만 아니라 다음과 같은 다른 애노테이션도 추가로 스캔한다.

  • @Controller
  • @Service
  • @Repository
  • @Configuration

실제로 컨트롤러 애노테이션을 확인해 보면

@Component 애노테이션을 포함하고 있다.
다른 애노테이션을 확인해 봐도 마찬가지로 @Component 애노테이션이 존재함을 확인할 수 있다.

'Back-End > Spring' 카테고리의 다른 글

빈 스코프  (0) 2023.09.08
빈 생명주기 콜백  (0) 2023.09.08
싱글톤 패턴과 싱글톤 컨테이너  (0) 2023.09.08
스프링 핵심 원리 -2  (0) 2023.09.01
스프링 핵심 원리 요약 - 1  (0) 2023.08.20

+ Recent posts