It's going to be one day 🍀

안녕하세요! 매일 매일 공부하려고 노력하는 백엔드 개발자 지망생의 공부 흔적입니다.

Back-End/Spring

[Spring] 의존성/컴포넌트 스캔/의존관계 주입/스프링부트 디렉터리

2jin2 2024. 2. 29. 16:32

의존성이란? : 프로젝트가 실행되기 위해 필요한 외부 라이브러리

내가 구현하지 않은 외부 코드를 가져다 쓰는 것이다. 의존성을 사용하면, 

- 코드의 재사용성 높이기 : 이미 개발된 외부 라이브러리를 사용하여 코드를 작성할 수 있기 때문에 코드의 재사용성을 높일 수 있음.

- 프로젝트의 안정성 높이기 : 외부 라이브러리의 버전 충돌을 방지하여 프로젝트의 안정성을 높일 수 있음.

- 프로젝트의 유지보수성을 높이기 : 외부 라이브러리의 업데이트 관리를 통해 프로젝트의 유지보수성을 높일 수 있음.

 

스프링 부트에서는 의존성 관리 목적으로 Gradle을 사용함. build.gradle이라는 파일에서 해주고있음.

 

build.gradle 파일

의존성, 플러그인, 빌드 스크립트 등이 정의되어 있음.

 

스프링 부트 스타터 살펴보기

Spring-Boot-Starter는 의존성이 모여있는 그룹이다.

스타터는 spring-boot-starter-(작업유형) 이라는 명명 규칙이 있음.


컴포넌트 스캔 (Component Scan) 이란?

빈(Bean)으로 등록할 클래스를 찾는 과정이다. 컴포넌트 스캔을 사용하면 자동으로 클래스를 탐색하고 빈으로 등록한다.

-> 이렇게 하면 개발자가 @Bean 어노테이션 하나하나 붙일 필요가 없다.

 

컴포넌트 스캔 대상

아래 애너테이션을 가진 클래스는 컴포넌트 스캔의대상이 되며 자동으로 빈이 등록됨.

  • @Component: 일반적인 스프링 빈 (이 어노테이션이 붙은 클래스는 전부 컴포넌트의 대상이 된다)
  • @Service: 비즈니스 로직을 담당하는 빈
  • @Repository: 데이터 액세스를 담당하는 빈
  • @Controller: Spring MVC 컨트롤러
  • @Configuration: 스프링 설정 정보

 

위의 애너테이션은 클래스의 용도에 따라 달라진다.

아래는 컴포넌트 스캔의 대상이 되는 @Service, @Repository, @Controller 애너테이션을 사용한 클래스들의 예시임.

import org.springframework.stereotype.Service;

@Service
public class UserService {
    public void addUser() {
    }
}
import org.springframework.stereotype.Repository;

@Repository
public class UserRepository {
    public void saveUser() {
    }
}
import org.springframework.stereotype.Controller;
import com.example.demo.service.UserService;

@Controller
public class UserController {
    
    private final UserService userService;
    
    public UserController(UserService userService) {
        this.userService = userService;
    }
    
    public void addUser() {
        userService.addUser();
    }
}

 

컴포넌트 스캔 옵션

옵션을 사용하여 스캔 대상을 제어할 수 있음. basePackages 옵션으로 컴포넌트 스캔을 수행할 패키지를 명시할 수 있음.

package com.estsoft.hello.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan(basePackages = "com.estsoft")
public class MyConfiguration {
}

-> 여기서는 ""com.estsoft"를 포함한 하위 클래스들이 스캔 대상이 됨.

 

주요한 컴포넌트 스캔의 옵션들

- basePackages

  • 컴포넌트 스캔을 수행할 패키지의 범위를 지정함. 만약 “com.example”로 지정했다면 “com.example”을 포함한 하위 클래스들이 스캔 대상이 됨.

- basePackageClasses

  • 특정 클래스가 속한 패키지부터 컴포넌트 스캔을 시작함.

- includeFilters

  • 스캔할 대상을 추가함.

- excludeFilters

  • 스캔에서 제외할 대상을 지정함.

ex) includeFiters, excludeFilters 옵션을 사용한 예시

@Configuration
@ComponentScan(
        basePackages = "com.example",
        includeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = MyComponent.class),
        excludeFilters = @ComponentScan.Filter(type = FilterType.REGEX, pattern = ".*Test"))
public class Config {

}

-> @MyComponent 애너테이션이 붙은 클래스를 스캔 대상으로 추가하고, “Test”로 끝나는 클래스를 스캔 대상에서 제외함.

여기서 주의할 점은 동일한 클래스가 두 옵션에 모두 해당하는 경우 excludeFilters가 우선 순위를 가짐! 그래서 excludeFilters에 해당하는 클래스들은 최종적으로 스캔 대상에서 제외됨.

 

컴포넌트 스캔으로 등록되는 빈의 이름

- 기본 규칙

클래스 이름의 첫 글자를 소문자로 변환하여 사용함 ex) UserService -> userService

 

-명시적 지정

@Component, @Service, @Repository, @Controller 등의 애너테이션을 사용하여 명시적으로 빈의 이름을 지정할 수 있음. ex) @Service("hello")

 

-동일한 이름의 빈이 이미 존재할 경우

최근 스프링 부트에서는 에러발생

 

스프링 부트에서의 컴포넌트 스캔

스프링 부트에서는 기본적으로 @SpringBootApplication 애너테이션이 붙은 클래스의 패키지와 하위 패키지를 컴포넌트 스캔 대상으로 지정한다. 이 애너테이션은 메인 클래스에서 사용함.

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class MyApplication {

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

 

->MyApplication 클래스의 패키지와 그 하위 패키지를 컴포넌트 스캔 대상으로 지정한다!


의존 관계 자동 주입

 

의존 관계란? 

내가 변하면 상대방에게도 영향을 끼치는 관계를 의미함.

 

의존 관계 주입 (Dependency Injection) 이란?

의존 객체를 직접 생성하는 것이 아니라 외부에서 해당 의존 객체를 주입받는 것을 말함.

1. 의존 객체를 직접 생성하는 방법

public class Car {
    private Engine engine;

    public Car() {
        // 의존 객체를 직접 생성
        this.engine = new Engine();
    }
}

-> Car 클래스 내에서 Engine 객체를 직접 생성하고 있음 

 

2. 의존 객체를 주입받는 방법

public class Car {
    private Engine engine;

    // 생성자를 통해 의존 객체를 주입받음
    public Car(Engine engine) {
        this.engine = engine;
    }
}

-> Car 클래스의 인스턴스를 생성할 때 외부에서 Engine 객체를 주입받아 사용함.

-> 이런 방법은 자동차가 엔진을 직접 선택하는 것이 아닌, 엔진 결정 권한을 외부에 위임하는 것과 같음.

 

스프링에는 빈(Bean)이라는 객체들이 있고 빈도 서로가 서로를 의존하고 있음. 빈의 의존 관계를 스프링에서 관리하는데, 이 때 의존 관계 주입 (Dependency Injection) 방식을 사용함.

 

의존 관계 자동주입

개발자가 일일이 의존 관계를 설정하지 않고 스프링이 자동으로 의존 관계를 주입하게 할 수 있음.

한마디로 new 생성자를 사용해서 생성을 하지 않아도, 개발자가 주입받은 객체를 사용할 수 있는 것!

의존 관계 주입에는 크게 3가지 방법이 있음!

 

1. 생성자 주입

public class MyClass {
    private MyDependency myDependency;

    public MyClass(MyDependency myDependency) {
        this.myDependency = myDependency;
    }
}

-> 생성자를 통해 주입한다. 가장 권장되는 방법이다.

 

만약 여러 개의 생성자가 있을 경우엔?

public class MyClass {
    private MyDependency myDependency;

    // @Autowired가 없는 생성자
    public MyClass(String message) {
        System.out.println("문자열을 받는 생성자 호출: " + message);
    }

    // @Autowired가 있는 생성자
    @Autowired
    public MyClass(MyDependency myDependency) {
        this.myDependency = myDependency;
        System.out.println("의존 객체를 받는 생성자 호출");
    }
}

-> 스프링에서는 @Autowired 애너테이션이 붙은 생성자를 사용한다.

 

2. 필드 주입

public class MyClass {
    @Autowired
    private MyDependency myDependency;
}

-> 멤버 변수에 @Autowired 애너테이션을 추가하면 자동적으로 의존 관계가 주입된다. 

 

3. Setter 주입 

public class MyClass {
    private MyDependency myDependency;

    @Autowired
    public void setMyDependency(MyDependency myDependency) {
        this.myDependency = myDependency;
    }
}

 

-> Setter 주입 방식의 경우 setter 메서드를 통해 주입된 객체를 수정할 수 있기 때문에 위험하다..

 


일반적으로 1번 생성자 주입 방식을 권장한다... 왜냐면 

 

불변 보장

- 한 번 의존 관계 주입이 되면 프로그램이 종료되기 전까지 변경되지 않아야 안전함.

- final 키워드와 함께 사용하면 주입된 객체가 불변함을 완벽하게 보장할 수 있음.

public class MyClass {
    // final 키워드 사용
    private final MyDependency myDependency;

    public MyClass(MyDependency myDependency) {
        this.myDependency = myDependency;
    }
}

 

테스트 용이성

- 생성자 주입 방식은 생성자가 여러 개가 아니라면 @Autowired를 생략할 수 있음.

 

순환 참조 방지

순환 참조 : 두 객체가 서로를 무한히 호출하는 무한 루프

- 생성자 주입 방식은 순환 참조 방지


@SpringBootApplication 이해하기

: 스프링 부트 애플리케이션을 구성하기 위한 어노테이션. 사용하면 다음과 같은 기능 활성화함.

  • Auto-configuration: 스프링 부트는 애플리케이션의 의존성을 기반으로 필요한 빈(Bean)을 자동으로 구성합니다.
  • Component Scanning: 스프링 부트 애플리케이션의 패키지를 스캔하여, 빈으로 등록할 수 있는 클래스를 찾습니다.
  • Main method: 스프링 부트 애플리케이션의 시작점으로 메인 메소드를 제공합니다.
@SpringBootApplication
public class SpringBootDeveloperApplication {
	public static void main(String[] args) {
		SpringApplication.run(SpringBootDeveloperApplication.class, args);
	}
}

-> 자바의 main() 메서드와 같은 역할을 함. 이곳에서 스프링 부트가 시작됨.

 

@SpringBootConfiguration

스프링 부트 관련 설정을 나타내는 어노테이션. @Configuration은 스프링 부트에서 규약 같은 것.

 

@ComponentScan

사용자가 등록한 빈을 읽고 등록하는 어노테이션. @Component라는 애노테이션을 가진 클래스들을 찾아 빈으로 등록해주는 역할을 함.

많이 쓰이는 어노테이션들

 

@EnableAutoConfiguration

스프링 부트에서 자동 구성을 활성화하는 어노테이션. 스프링 부트 서버가 실행될 때 스프링 부트의 메타 파일을 읽고 정의된 설정들을 자동으로 구성하는 역할을 함.


RestController 살펴보기

같이 실습해보기~

package com.estsoft.hello.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class TestController {
    @GetMapping("/spring")
    public String spring() {
        return "hello";
    }
}

 

-> 이 코드를 돌리고 웹에서 검색했을 때 localhost:8080/spring 웹페이지를 찾을 수 없는 이유는?

-> Controller가 "hello"라는 웹페이지를 찾아서 열려고 시도하기 때문에 찾을 수 없다. hello라는 웹페이지가 없어서 404 NOT FOUND 오류가 뜬다.

 

< Controller vs RestController 차이 정리>

- @Controller : 이름에 맞는 동적 페이지 전달하기 위해 사용됨. (주로 동적인 웹 페이지 생성에 사용)  

- @RestController : 데이터를 반환하기 위해 사용됨. (주로 웹 API 어플리케이션 간에 데이터를 주고받을 때 사용) 객체


스프링 부트 프로젝트 디렉터리 구성하며 살펴보기

스프링 부트는 각 계층이 양 옆의 계층과 통신하는 구조를 따름.

계층은 각자의 역할과 책임이 있는 어떤 소프트웨어의 구성 요소를 의미함. 각 계층은 서로 소통할 순 있지만 직접적으로 영향을 미치진 않음.

실제로 컨트롤러, 서비스, 레파지토리 각각의 패키지를 만들어주는 것이 좋음.

 

프레젠테이션 계층

HTTP 요청을 받고 이 요청을 비즈니스 계층으로 전송하는 역할을 함.

관례상 XxxController 클래스명으로 지음.

 

비즈니스 계층

모든 서비스를 만들기 위한 비즈니스 로직을 처리함. ex) 웹 사이트에서 벌어지는 모든 작업.

관례상 XxxService 라는 클래스명으로 지음.

 

퍼시스턴스 계층

모든 데이터베이스 관련 로직을 처리함. 이 과정에서 데베에 접근하는 DAO 객체를 사용할수도 있음.

DAO : 데이터베이스 계층과 상호작용하기 위한 객체

 

정리

- 개념의 영역 : 계층

- 실제 구현을 위한 영역 : 컨트롤러, 서비스, 레파지토리

 

스프링 부트 프로젝트 디렉터리 구성 

 

 

 

main

실제 코드를 작성하는 공간. 프로젝트 실행에 필요한 소스 코드나 리소스 파일은 모두 이 폴더 안에 들어있음.

test

프로젝트의 소스 코드를 테스트할 목적의 코드나 리소스 파일이 들어 있음.

build.gradle

빌드를 설정하는 파일. 의존성이나 플러그인 설정 등과 같이 빌드에 필요한 설정을 할 때 사용함.

settings.gradle

빌드할 프로젝트의 정보를 설정하는 파일.


스프링 부트 프로젝트 발전시키기!!

의존성을 추가한 다음에 프레젠테이션 계층, 비즈니스 계층, 퍼시스턴스 계층 순서대로 코드를 추가할 것임

 

1) build.gradle에 의존성 추가하기

0229 여기까지 완료

롬복 추가까지 완료