SpringBootApplication 시작

h.Im·2024년 8월 23일

Springboot 기초

목록 보기
1/17
post-thumbnail
@SpringBootApplication
public class MyServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(MyServerApplication.class, args);
    }
}

스프링부트 강의 2일차에 위 코드로 스프링부트 어플리케이션을 시작하는 법을 배웠습니다. 위 코드가 어떤 동작을 수행하는지 알아보도록 하겠습니다.


@SpringBootApplication

위 어노테이션을 아래 세 가지 어노테이션을 결합한 것입니다.

  • @Configuration : 해당 클래스가 Spring의 설정 클래스임을 나타냄
  • @EnableAutoConfiguration : Spring Boot가 애플리케이션의 의존성에 따라 자동으로 설정을 해주도록 합니다. 예를 들어, 애플리케이션에 웹 관련 라이브러리가 포함되어 있으면 자동으로 내장된 웹 서버를 설정합니다.
  • @ComponentScan : 해당 패키지와 그 하위 패키지에서 Spring의 @Component 어노테이션이 붙은 클래스를 자동으로 스캔하고 빈으로 등록합니다.

@SpringbootApplication의 속성들

  • exclude
    특정 자동 구성 클래스를 제외할 때 사용됩니다. 예를 들어 @SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})와 같이 작성 시, 데이터베이스 연결을 자동으로 설정하는 DataSourceAutoConfiguration을 제외합니다.
  • excluideName
    클래스의 이름을 문자열로 지정하여 자동 구성을 제외할 수 있는 설정입니다. 예를 들어 @SpringBootApplication(excludeName = {"com.example.SomeAutoConfiguration"})와 같이 작성 시, SomeAutoConfiguration이라는 클래스를 자동 구성에서 제외합니다.
  • scanBasePackages
    특정 패키지를 기준으로 컴포넌트를 스캔하도록 지정하는 속성입니다. 기본적으로는 @SpringBootApplication이 선언된 패키지와 그 하위 패키지를 스캔하지만, 직접 지정이 필요한 경우 사용하면 되겠습니다.
  • scanBasePackageClasses
    패키지 경로 대신 특정 클래스들이 속한 패키지를 기준으로 컴포넌트를 스캔하도록 지정할 수 있습니다.
  • nameGenerator
    nameGenerator 속성은 @ComponentScan에서 컴포넌트 이름을 생성하는 방식을 커스터마이징할 때 사용됩니다.
  • proxyBeanMethods
    proxyBeanMethods 속성은 @Configuration 클래스 내에서 메서드 간의 호출이 프록시를 통해 처리되는지 여부를 제어합니다.

내장 Tomcat 서버 설정 및 실행, 의존성이 추가된 것들에 대한 기본 설정 등등 개발자가 비즈니스 로직에 집중할 수 있도록 설정을 도와주는 어노테이션으로 이해하고, 아직 이해가 안 가는 내용이 많으니 우선 암기하듯이 기억하고 넘어가도록 하겠습니다.


다음 코드들로 Spring Boot의 레이어드 아키텍처의 전형적인 예시를 살펴보도록 하겠습니다.

Controller

@Slf4j
@CrossOrigin
@AllArgsConstructor
@RestController
@RequestMapping("/")
public class MyController {
    private final MyService service;

    @PostMapping
    public ResponseEntity<MyResponse> create(@RequestBody MyRequest request) {
        log.info("CREATE");

        if (ObjectUtils.isEmpty(request.getTitle()))
            return ResponseEntity.badRequest().build();
        MyModel result = this.service.add(request);
        return ResponseEntity.ok(new MyResponse(result));
    }

    @GetMapping("{id}")
    public ResponseEntity<MyResponse> readOne(@PathVariable Long id) {
        log.info("READ ONE");
        MyModel result = this.service.searchById(id);
        return ResponseEntity.ok(new MyResponse(result));
    }

    @DeleteMapping
    public ResponseEntity<?> deleteAll() {
        log.info("DELETE ALL");
        this.service.deleteAll();
        return ResponseEntity.ok().build();
    }
}

Cotroller 레이어의 역할은 HTTP 요청을 받아서 처리하고, 그에 대한 응답을 반환하는 것입니다.

어떻게 그 역할을 맡게 된걸까?

@RestController 어노테이션에 의해 HTTP 요청을 받아 처리하게 됩니다. @RestController은 @Controller, @ResponseBody의 조합입니다.

  • @Controller : 이 클래스가 컨트롤러 역할을 한다고 Spring에게 알려주는 역할
  • @ResponseBody : 이 클래스의 메소드들이 반환하는 값은 HTTP 응답 본문에 쓰임을 알려주는 역할

Spring 애플리케이션에 들어오는 HTTP 요청을 DispatcherServlet 이라는 서블릿이 먼저 받고, 이 서블릿이 적절한 컨트롤러에게 요청을 전달합니다. 적절한 컨트롤러를 찾는 것은 Handler Mapping에 의해 이루어지는데, @RequestMapping 어노테이션을 통해 어느 컨트롤러의 어느 메서드가 특정 요청을 처리할지 결정합니다.

위 예시 코드에서는 / 경로로 들어오는 요청을 MyController가 받고, 내부적으로 @PostMapping, @GetMapping("{id}"), @DeleteMapping 어노테이션을 통해 메소드가 매핑되어 있습니다.


Service

@Service
@AllArgsConstructor
public class TodoService {
    private final MyRepository myRepository;

    public MyModel add(MyRequest request) {
        MyModel myModel = new MyModel();
        myModel.setTitle(request.getTitle());
        myModel.setOrder(request.getOrder());
        myModel.setCompleted(request.getCompleted());
        return this.myRepository.save(myModel);
    }

    public MyModel searchById(Long id) {
        return this.myRepository.findById(id)
                .orElseThrow(() -> new ResponseStatusException(HttpStatus.NOT_FOUND));
    }

    public void deleteAll() {
        this.myRepository.deleteAll();
    }
}

Service 레이어의 역할은 비즈니스 로직을 처리하고, 데이터의 생성, 조회, 수정, 삭제 등의 작업을 수행하는 것입니다.

어떻게 그 역할을 맡게 된걸까?

@Service 어노테이션을 통해 이 클래스가 서비스 계층임을 나타내고, Spring이 관리하는 빈으로 등록하게 됩니다.

빈이란?

주요 개념

  • IoC(Inversion of Control, 제어의 역전)
    객체의 생성과 그 객체들 간의 의존성 관리를 개발자가 직접 수행하지 않고, Spring이 대신 처리해 주는 개념입니다. 객체의 생명주기와 의존성 주입을 Spring이 대신 해주기 때문에, 개발자는 비즈니스 로직에 집중할 수 있습니다.
  • DI(Dependency Injection, 의존성 주입)
    객체 간의 의존 관계를 Spring이 주입해주는 방식입니다. @Component, @Service, @Repository 등의 어노테이션이 붙은 클래스는 컴포넌트 스캔에 의해 빈으로 등록되고, Spring이 빈의 생명주기를 관리합니다.

Repository

@Repository
public interface MyRepository extends JpaRepository<MyModel, Long> {

}

데이터베이스와의 상호작용을 담당하는 레이어입니다. JPA를 통해 DB와 상호작용합니다.

어떻게 그 역할을 맡게 된걸까?

우선 MyRepository 클래스는 @Repository 어노테이션을 통해 DB와의 상호작용을 위한 빈으로 등록됩니다.
JpaRepository<MyModel, Long>을 상속 받는다는 것은, Spring Data JPA가 제공하는 레포지토리 인터페이스가 됨을 의미합니다.
JpaRepository를 ctrl+클릭하면 아래 세 가지를 상속받고 있음을 확인할 수 있었습니다.

  • ListCrudRepository<T, ID>
  • ListPagingAndSortingRepository<T, ID>
  • QueryByExampleExecutor

이를 통해 기본적인 CRUD 메서드가 제공되고, 페이징과 정렬 기능이 제공되며, 배치 작업, 플러시 관리 등을 추가로 지원받게 됩니다.

<MyModel, Long> 부분은
MyModel: 엔터티 클래스를 의미하며, JpaRepository가 처리하는 테이터 타입이 이 엔터티 클래스가 됩니다.
Long: 엔티티의 기본 키의 타입

위 서비스 코드를 다시 살펴보겠습니다.

private final MyRepository myRepository;

  public MyModel add(MyRequest request) {
      MyModel myModel = new MyModel();
      myModel.setTitle(request.getTitle());
      myModel.setOrder(request.getOrder());
      myModel.setCompleted(request.getCompleted());
      return this.myRepository.save(myModel);
  }

myRepository.save 함수가 호출될 수 있었던 이유가 JpaRepository를 상속받았기 때문이며 myModel의 타입이 MyModel 임을 확인할 수 있습니다.

0개의 댓글