본문 바로가기
CS/Spring Boot

[스프링 부트] 스프링 부트 1

by Hoozy 2023. 4. 6.

스프링 부트 로드맵

https://roadmap.sh/spring-boot

스프링 부트의 로드맵에 따라서 따로 배워보겠습니다.

 

카테고리 : Spring Boot

스프링 코어(컨테이너) (IoC 컨테이너)
  • 객체를 생성하고 관리하고 책임지고 의존성을 관리해주는 컨테이너.
    • 컨테이너 : 객체의 생명주기를 관리, 생성된 인스턴스들에게 추가적인 기능을 제공하도록 하는 것.
  • 인스턴스 생성부터 소멸까지의 인스턴스 생명주기 관리를 개발자가 아닌 컨테이너가 대신 해준다.
  • 객체관리 주체가 프레임워크(Container)가 되기 때문에 개발자는 로직에 집중할 수 있는 장점이 있다.
  • 특징
    1. IoC 컨테이너는 객체의 생성을 책임지고, 의존성을 관리한다.
    2. POJO의 생성, 초기화, 서비스, 소멸에 대한 권한을 가진다.
      • POJO : 순수한 자바로 이루어진 객체 -> Bean, getter, setter 등
    3. 개발자들이 직접 POJO를 생성할 수 있지만 컨테이너에게 맡긴다.
    4. 개발자는 비즈니스 로직에 집중할 수 있다.
    5. 객체 생성 코드가 없으므로 TDD가 용이하다.

IoC의 분류

  • DL(Dependency Lookup)
    • 저장소에 저장되어 있는 Bean에 접근하기 위해 컨테이너가 제공하는 API를 이용하여 Bean을 Lockup하는 것
  • DL(Dependency Injection)
    • 의존성 주입
    • 각 클래스간의 의존관계를 빈 설정(Bean Definition) 정보를 바탕으로 컨테이너가 자동으로 연결해주는 것
    1. Setter Injection (수정자 주입)
    2. Constructor Injection (생성자 주입)
    3. Method Injection (필드 주입)
  • DL 사용시 컨테이너 종속이 증가하기 때문에 주로 DI를 사용한다.

스프링 컨테이너의 종류

  • Bean : 스프링 컨테이너가 관리하는 객체.
  • 빈들을 관리하는 의미로 컨테이너를 빈 팩토리로 부른다.
    • 객체의 생성과 객체 사이의 런타임 관계를 DI 관점에서 볼 때 컨테이너를 빈 팩토리라고 한다.
    • 빈 팩토리에 여러가지 컨테이너 기능을 추가한 어플리케이션컨텍스트(ApplicationContext)가 있다.

BeanFactory와 ApplicationContext

  1. BeanFactory
  • BeanFactory 계열의 인터페이스만 구현한 클래스는 단순히 컨테이너에서 객체를 생성하고 DI를 처리하는 기능만 제공한다.
  • Bean을 등록, 생성, 조회, 반환 관리를 한다.
  • 팩토리 디자인 패턴을 구현한 것으로 BeanFactory는 빈을 생성하고 분배하는 책임을 지는 클래스이다.
  • Bean을 조회할 수 있는 getBean() 메소드가 정의되어 있다.
  • 보통은 BeanFactory를 바로 사용하지 않고, 이를 확장한 ApplicationContext를 사용한다.
  1. ApplicationContext
  • Bean을 등록, 생성, 조회, 반환 관리하는 기능은 BeanFactory와 같다.
  • 스프링의 각종 부가 기능을 추가로 제공한다.
  • BeanFactory 보다 더 추가적으로 제공하는 기능
    • 국제화가 지원되는 텍스트 메시지를 관리 해준다.
    • 이미지같은 파일 자원을 로드할 수 있는 포괄적인 방법을 제공해준다.
    • 리스너로 등록된 빈에게 이벤트 발생을 알려준다.
  • 따라서 대부분의 어플리케이션에서는 빈팩토리 보다는 어플리케이션콘텍스트를 사용하는 것이 더 좋다.
Bean
  • 자바 어플리케이션은 어플리케이션 동작을 제공하는 객체들로 이루어져 있다. 이때 객체들은 독립적으로 동작하는 것 보다 서로 상호작용하여 동작하는 경우가 많다. 이렇게 상호작용하는 객체를 '객체의 의존성'이라고 표현한다.
  • 스프링에서 스프링 컨테이너에 객체들을 생성하면 객체끼리 의존성을 주입(DI)하는 역할을 해준다.
  • 이때 스프링 컨테이너에 등록한 객체들을 빈 이라고 한다.

스프링 컨테이너에 Bean을 등록하는 두 가지 방법

1. 컴포넌트 스캔과 자동 의존관계 설정

  • 스프링에서 사용자 클래스를 스프링 빈으로 등록하는 가장 쉬운 방법은 클래스 선언부 위에 @Component 어노테이션을 사용하는 것이다.
  • @Controller, @Service, @Repository 세 가지 모두 @Component를 포함하고 있으며, 위에 네 가지 모두 스프링 컨테이너에 의해 자동으로 생성되어 스프링 빈으로 등록된다.

2. 자바 코드로 직접 스프링 빈 등록

  • 수동으로 스프링 빈을 등록하려면 자바 설정 클래스를 만들어 사용해야 한다.
  • 클래스 선언부 위에 @Configuration 어노테이션을 클래스 선언부 위에 추가하고, 특정 타입을 리턴하는 메소드를 만들고, @Bean 어노테이션을 붙여주면 자동으로 해당 타입의 빈 객체가 생성된다.
의존성 주입

1. @Autowired 어노테이션

  • 스프링은 @Autowired 어노테이션을 이용한 다양한 의존성 주입 방법을 제공한다. 이를 통해 객체 간의 결합도를 줄이도 코드의 재활용성을 높일 수 있다. 이때 @Autowired는 스프링에게 의존성을 주입하는 지시자 역할로 쓰인다.

의존성 주입을 해야하는 이유

  1. 테스트가 용이하다.
  2. 코드의 재사용성을 높여준다.
  3. 객체 간의 의존성(종속성)을 줄이거나 없앨 수 있다.
  4. 객체 간의 결합도를 낮추면서 유연한 코드를 작성할 수 있다.

의존성 주입의 3가지 방법

  1. 생성자 주입(Constructor Injection)
@Controller
public class TestController {
    //final을 붙일 수 있음
    private final TestService testService;

    //@Autowired 
    public TestController(TestService testService) {
        this.testService = testService;
    }
}
  • 클래스의 생성자가 하나이고, 그 생성자로 주입받을 객체가 빈으로 등록되어 있자면 @Autowired를 생략할 수 있다.
  • 스프링 프레임워크에서 가장 권장하는 방법.
  1. 필드 주입(Field Injection)
@Controller
public class TestController {

    @Autowired 
    private TestService testService;
}
  • 필드에 @Autowired 어노테이션만 붙여주면 자동으로 의존성 주입된다.
  • 가장 많이 쓰인다.
  • 단점
    1. 코드가 간결하지만, 외부에서 변경하기 힘들다.
    2. 프레임워크에 의존적이고 객체지향적으로 좋지 않다.
  1. 수정자 주입(Setter Injection)
@Controller
public class TestController {
    private TestService testService;

    @Autowired
    public void setTestService(TestService testService) {
        this.testService = testService;
    }
}
  • Setter 메소드에 @Autowired 어노테이션을 붙이는 방법이다.
  • 단점
    • 수정자 주입을 사용하면 setter를 public으로 열어두어야 하기 때문에 언제 어디서든 변경이 가능하다.

가장 좋은 주입 방식

  • 스프링 프레임워크에서 권장하는 방법은 생성자 주입 방법이다.
  • 아래와 같은 이유로 생성자 주입의 사용을 권장한다.
  1. 순환 참조를 방지할 수 있다.
  • 개발을 하다 보면 여러 컴포넌트 간에 의존성이 생긴다.
  • 만약, A가 B를 참조하고, B가 A를 참조한다고 가정하면, 필드 주입과 수정자 주입은 빈이 생성된 후에 참조를 하기 때문에 어플리케이션이 아무런 오류, 경고 없이 구동된다. -> 실제 코드가 호출될 때까지 문제를 알 수 없다.
  • 하지만 생성자 주입은 실행하면 BeanCurrentlyInCreationException이 발생하게 된다.
  • 순환 참조 뿐만 아니라 의존 관계에 내용을 외부로 노출 시킴으로서 어플리케이션을 실행하는 시점에서 오류를 체크할 수 있다.
  1. 불변성(Immutability)
  • 생성자로 의존성을 주입할 때 final로 선언할 수 있고, 이 때문에 런타임에서 의존성을 주입받는 객체가 변할 일이 없어지게 된다.
  • 하지만 수정자 주입이나 일반 메소드 주입을 이용하게 되면 불필요하게 수정의 가능성을 열어두게 되어서 OOP의 원칙 중 OCP(계방-폐쇄의 원칙)를 위반하게 된다.
  • 따라서 생성자 주입을 통해 변경의 가능성을 배제하고 불변성을 보장하는 것이 좋다.
  • 또한 필드 주입은 null이 만들어질 가능성이 있는데, final로 선언한 생성자 주입은 null이 불가능하다.
  1. 테스트에 용이하다.
  • 생성자 주입을 사용하게 되면 테스트 코드를 좀 더 편리하게 작성할 수 있다.
  • 독립적으로 인스턴스화가 가능한 POJO를 사용하면, DI 컨테이너 없이도 의존성을 주입하여 사용할 수 있다.
  • 이를 통해 코드 가독성이 높아지며, 유지보수가 용이하고 테스트의 격리성과 예측 가능성을 높일 수 있다는 장점이 생기게 된다.

2. @RequiredArgsConstructor 어노테이션

  • @Autowired 의 생성자 주입의 단점은 생성자를 만들기 번거롭다는 것이다.
  • 이를 보완하기 위해 lombok을 사용하여 간단한 방법으로 생성자 주입을 할 수 있다.
  • @RequiredArgsConstructor 어노테이션은 final이 붙거나 @NotNull이 붙은 필드의 생성자를 자동 생성해줘서 의존성 주입해주는 어노테이션이다.
@RequiredArgsConstructor
public class TestController {

    private final TestService testService;
}
AOP (Aspect-Oriented Programming)
  • 관심지향 프로그래밍으로, OOP를 돕는 보조적인 기술이며, 관심사의 분리(기능의 분리)의 문제를 해결하기 위해 만들어진 프로그래밍 패러다임이다.
  • 기능을 핵심 관심 사항과 공통 관심 사항으로 분리시키고 각각을 모듈화하는 것을 의미.
    • 핵심 기능 : 업무 로직을 포함하는 기능
    • 부가 기능 : 핵심 기능을 도와주는 부가적인 기능
    • OOP를 적용하여도 핵심 기능에서 부가 기능을 쉽게 분리된 모듈로 작성하기 어려운 문제점을 AOP가 해결해준다.
    • 부가 기능을 애스펙트(Aspect)로 정의하여, 핵심 기능에서 부가 기능을 분리함으로써 핵심 기능을 설계하고 구현할 때 객체지향적인 가치를 지킬 수 있게 도와주는 개념이다.
  • 필수적이지만 어쩔 수 없이 반복적으로 사용되는 코드들을 리팩토링할 수 있도록 해준다.
  • 이로인해 여러 곳에서 사용될만한 코드들이 한 곳에서 유지하고 관리할 수 있는 이점을 갖게 된다.

AOP의 특징

  1. 프록시 패턴 기반
  • 스프링은 타겟(Target) 객체에 대한 프록시를 만들어서 제공한다.
  • 타겟을 감싸는 프록시는 실행시간(RunTime)에 생성된다.
  • 프록시는 어드바이스(Advice)를 타겟 객체에 적용하면서 생성되는 객체
  • 프록시 객체를 쓰는 이유는 접근 제어 및 부가기능을 추가하기 위함이다.
  1. 프록시가 호출을 가로챔(Intercept)
  • 프록시는 타겟 객체에 대한 호출을 가로챈 다음 Advice의 부가기능 로직을 수행하고난 후에 타겟의 핵심기능 로직을 호출한다.(전처리 어드바이스)
  • 타겟의 핵심기능 로직 메소드를 호출한 후에 부가기능을 수행하는 경우도 있다.(후처리 어드바이스)
  1. 메소드 JoinPoint만 지원한다.
  • 스프링은 동적 프록시를 기반으로 AOP를 구현하므로 메소드 조인 포인트만 지원
  • 핵심기능(타겟)의 메소드가 호출되는 런타임 시점에만 부가기능(어드바이스)를 적용할 수 있음
  • 반면에 AseptJ같은 고급 AOP 프레임워크를 사용하면 객체의 생성, 필드값의 조회와 조작, static 메소드 호출 및 동기화 등의 다양한 작업에 부가기능을 적용할 수 있다.

AOP의 용어

  1. Target
  • 부가기능을 부여할 대상 (핵심기능을 담고 있는 모듈)
  1. Aspect
  • 부가기능 모듈을 Aspect라고 부른다. (핵심기능에 부가되어 의미를 갖는 모듈)
  • 부가될 기능을 정의한 Advice와 Advice를 어디에 적용할지를 결정하는 PointCut을 함께 갖고 있다.
  • 어플리케이션의 핵심적인 기능에서, 부가적인 기능을 분리해서 Aspect라는 모듈로 만들어서 설계하고 개발하는 방법
  1. Advice
  • 실질적으로 부가기능을 담은 구현체
  • 타겟 오브젝트에 종속되지 않기 때문에, 부가기능에만 집중할 수 있음
  • Aspect가 무엇을 언제 할지를 정의
  1. PointCut
  • 부가기능이 적용될 대상(Method)을 선정하는 방법
  • Advice를 적용할 JoinPoint를 선별하는 기능을 정의한 모듈
  1. JoinPoint
  • Advice가 적용될 수 있는 위치
  • Spring에서는 메소드 조인포인트만 제공한다.
  • 타겟 객체가 구현한 모든 메소드는 조인 포인트가 된다.
  1. Proxy
  • Target을 감싸서 Target의 요청을 대신 받아주는 랩핑 오브젝트.
  • 클라이언트에서 Target을 호출하게되면, 타겟이 아닌 타겟을 감싸고 있는 Proxy가 호출되어,
  • 타겟메소드 실행 전에 선처리, 후처리를 실행한다.
  1. Introduction
  • 타겟 클래스에 코드변경없이 신규메소드나 멤버변수를 추가하는 기능
  1. Weaving
  • 지정된 객체에 Aspect를 적용해서, 새로운 프록시 객체를 생성하는 과정
  • Spring AOP는 런타임에서 프록시 객체가 생성된다.

AOP 사용 방법

  1. Gradle 적용
implementation 'org.springframework.boot:spring-boot-starter-aop'
  1. @EnableAspectJAutoProxy 어노테이션 추가
  2. 클래스 선언부 이전에 @Aspect 어노테이션(AOP 클래스로 설정)과 @Conponent를 추가해준다.
추가적인 어노테이션

@RestController

  • 현재 클래스를 rest api로 만드는 어노테이션이다.
  • 반환값은 json 형태로 반환되게 된다.

@RequestMapping

  • @RequestMapping(value = "", method = RequestMethod.GET) 형식에, value 안에 요청 url을 넣으면 매핑된다. -> GET은 POST 로 변경도 가능하다.
  • @GetMappiing() : @RequestMapping(value = "", method = RequestMethod.GET) 와 같다. -> GET 방식
  • @PostMapping() : @RequestMapping(value = "", method = RequestMethod.POST) 와 같다. -> POST 방식

@ResponseBody, @RequestBody

  • POST방식은 데이터를 body에 담아서 보내는데 이때 body를 응답(@ResponseBody)받고, 요청(@RequestBody)할 때 사용하는 어노테이션이다.
  • @ResponseBody 는 리턴 값이 View 값이 아닌, json, xml 같은 데이터를 반환한다. -> ajax에 쓰인다.

@RequestParam, @PathVariable

  • @RequestParam : url www.aaa.com?name=이름 여기서 name 값을 가져올 때 사용한다.
@GetMapping("/name")
public String getName(@RequestParam("name") String name) { // 사용 예시

}
  • @PathVariable : 주로 api url www.aaa.com/user/이름/33 여기서 name인 이름, age인 33을 가져올 때 사용한다.
@GetMapping("/user/{name}/{age}") // 형태로 매핑한다.
public String getName(@PathVariable("name") String name, @PathVariable("age") int age) { // 사용 예시

}
스프링 MVC 프레임워크
  • MVC 디자인 패턴을 따르며 스프링 프레임워크의 특징도 가지고 있다.

1. DispatcherServlet(Front Controller)

  • 다른 웹 프레임워크들에서 사용되는 Front Controller의 역할을 한다.
  • 가장 앞단에서 클라이언트의 요청을 처리하는 Controller로서 요청부터 응답까지 전반적인 처리 과정을 통제한다.

2. Handler(Controller)

  • Spring MVC에서는 Controller=Handler(핸들러)라고 생각하면 편하다. 핸들러는 DispatcherServlet가 전달해준 HTTP 요청을 처리하고 결과를 Model에 저장한다.

3. ModelAndView

  • ModelAndView는 controller에 의해 반환된 Model과 View가 Wrapping 된 객체이다. 말 그대로 Model과 View가 같이 들어있는 객체라고 보면 된다.

4. ViewResolver

  • ViewResolver는 ModelAndView를 처리하여 View를 그리는 역할을 한다. Model에 저장된 데이터를 사용해 View를 그리고 사용자는 View를 보게 된다.

Spring MVC 동작 순서

  1. 핸들러 조회 : 핸들러 매핑을 통해 요청 URL에 매핑된 핸들러(컨트롤러)를 조회
  2. 핸들러 어댑터 조회 : 핸들러를 실행할 수 있는 핸들러 어댑터를 조회한다.
  3. 핸들러 어댑터 실행 : 핸들러 어댑터를 실행한다.
  4. 핸들러 실행 : 핸들러 어댑터가 실제 핸들러를 실행한다.
  5. ModelAndView 반환 : 핸들러 어댑터는 핸들러가 요청을 처리하고 반환하는 정보를 ModelAndView로 변환해서 반환한다.
  6. viewResolver 호출 : viewResolver를 찾고 실행한다.
  7. View반환 : viewResolver는 View의 논리 이름을 물리 이름으로 바꾸고, 렌더링 역할을 담당하는 View 객체를 반환한다.
  8. View 렌더링 : View를 통해서 View를 렌더링한다.

핸들러 매핑 , 핸들러 어댑터

  • 핸들러 매핑 : DispatcherServlet이 요청에 맞는 핸들러를 찾는 과정이다.
  • 핸들러 어댑터 : 핸들러 매핑에서 찾은 핸들러 객체를 다룰 수 있는 어댑터이다.
  • 다양한 핸들러의 return 타입은 String, ModelAndView 등 다양하며, Parameter로 들어오는 값도 HttpServletRequest로 받거나 HttpServletResponse로 받는 등 다양한 형식으로 받을 수 있다.
  • 이처럼 다양한 핸들러 구현 방식에 맞춰 개발을 하는 것은 쉽지 않다.
  • 따라서 이러한 요구 사항을 충족하기 위해 중간에 Adapter를 두는 어댑터 패턴을 사용하는 것이다.
  • 만약 핸들러 어댑터를 사용하면, 핸들러에서 결괏값을 어떻게 반환하든 이를 처리해 DispatcherServlet에게 ModelAndView객체로 통일하여 응답을 전달해 줄 수 있다.
  • 스프링은 이미 필요한 핸들러 매핑과 핸들러 어댑터를 대부분 구현해주어서 개발자가 직접 핸들러 매핑과 핸들러 어댑터를 구현하는 일은 거의 없다.

빈 스코프 (Bean Scope)

  • 빈이 컨테이너에 의해 관리되는 범위이다.

빈 스코프 종류

  1. 싱글톤
    • 기본 스코프, 스프링 컨테이너의 시작과 종료까지 유지되는 가장 넓은 범위의 스코프이다.
  2. 프로토타입
    • 스프링 컨테이너는 프로토타입 빈의 생성과 의존관계 주입까지만 관여하고 더는 관리하지 않는 매우 짧은 범위의 스코프이다(따라서 빈 콜백중 종료메서드가 호출이 안된다.).
    • 클래스 선언부 위에 Scope("prototype") 어노테이션을 추가하면 자동 등록된다.
  3. 웹 관련 스코프
    • request: 웹 요청이 들어오고 나갈때 까지 유지되는 스코프이다.
    • session: 웹 세션이 생성되고 종료될 때 까지 유지되는 스코프이다.
    • application: 웹의 서블릿 컨텍스와 같은 범위로 유지되는 스코프이다.

다음 게시글 스프링 부트 2

https://hoozy.tistory.com/entry/%EC%8A%A4%ED%94%84%EB%A7%81-%EB%B6%80%ED%8A%B8-%EC%8A%A4%ED%94%84%EB%A7%81-%EB%B6%80%ED%8A%B8-2

 

[스프링 부트] 스프링 부트 2

이전 게시글 스프링 부트 1 https://hoozy.tistory.com/entry/%EC%8A%A4%ED%94%84%EB%A7%81-%EB%B6%80%ED%8A%B8-%EC%8A%A4%ED%94%84%EB%A7%81-%EB%B6%80%ED%8A%B8-1 카테고리 : Spring Boot Spring Security 인증(Authentication) : 해당 사용자가 본

hoozy.tistory.com

참고 자료

https://dev-coco.tistory.com/80
https://dev-coco.tistory.com/69
https://dev-coco.tistory.com/70
https://dev-coco.tistory.com/81
https://chung-develop.tistory.com/56
https://code-lab1.tistory.com/258

'CS > Spring Boot' 카테고리의 다른 글

[스프링 부트] 스프링 부트 3  (0) 2023.04.09
[스프링 부트] 스프링 부트 2  (0) 2023.04.08

댓글