Framework/Spring (Java)

우리는 왜 스프링을 사용하는가? - Java spring의 특징

joyCHAE 2021. 3. 25. 02:06

 

앞선 글에서 왜 우리가 프레임워크를 사용해야 하는 지 알아보았다.

짧게 짚고 넘어가자보자. 결국 개발자 입장에서는 검증된 코드를 이용해서 최소한의 부분만 바꾸어 빠르게 개발하기 위해서이며, 회사 입장에서는 누구에게 개발을 맡기던 일정 수준 이상의 결과물을 기대할 수 있기 때문이다.

 

그렇다면 Spring(Java), Django(Python), Flask(Python), Ruby on rails(Ruby), .NET Framwork 등 수많은 프레임워크 중 Spring은 어떤 장점이 있길래 사람들이 사용하는 것일까?

 

 

그렇다면 프레임 워크 중에서 왜 자바 스프링인가?

 

스프링은 여러 프레임워크들 중 자바(Java)를 기반으로 하는 프레임워크이다.

우선, 자바 스프링의 특징부터 알아보자. 이러한 특징들 덕분에 프로그래머는 더욱 편하게 서버 개발이 가능하다.

 

 

1. POJO기반의 구성 (Plain Old Java Object)

 

코드를 개발할 때, 개발자가 특정한 라이브러리나 컨테이너의 기술에 종속적이지 않음을 의미한다. Java코드를 이용해서 객체를 구성하는 방식 그대로 스프링에서 사용할 수 있다.


덕분에, 자유롭게 객체지향적 설계를 구현할 수 있다. 무슨 말이냐 하면, 개발자는 가장 일반적인 형태로 코드를 작성하고 실행할 수 있다. 때문에 높은 생산성과 유연한 테스트를 할 수 있다는 장점을 가지게 된다.

 

 

 

2. DI(Dependency Injection, 의존성 주입)을 통한 객체 관계 구성

 

"DI는 객체지향 프로그래밍의 강력한 지원군이다."

 

의존성 주입은 제어의 역전이 일어나는것을 전제로 스프링 내부의 객체들 간의 관계를 관리할 때 사용한다. 의존성 주입은 특정 개체에 필요한 객체를 외부에서 결정하여 연결시키는 것을 말한다. 자바에서는 인터페이스를 사용하려 의존적인 관계를 처리한다.

 

  • 메소드나 객체(bean)의 호출 작업은 제어의 역전을 통해 외부에서 이루어진다. (여기서 외부라 함은 객체를 기준으로 봤을 때의 외부를 의미한다.)

  • 제어의 역행을 전제조건으로 의존성 주입이 일어난다.

  • 의존성을 가진 객체에 대해 스프링에서 의존성 주입이 발생하도록 한다.

  • 의존성 주입 특싱으로 인해 개발자가 POJO 개발이 가능하게 된다.

실무에서는 주로 정형화된 컨트롤러, 서비스, 리포지토리 같은 코드는 컨포넌트 스캔을 사용한다. 그리고 정형화 되지 않거나, 상황에 따라 구현 클래스를 변경해야 하면 설정을 통해 스프링 빈으로 등록한다.

 

 

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

 

예를 들어, MemberController의 생성자에 @Autowired를 붙임으로써, 스프링은 스프링이 저장하고 있는 memberSevice 객체를 컨트롤러가 생성될 때 가져와 준다. 즉, 생성자에 @Autowired가 있으면 스프링이 연관된 객체를 스프링 컨테이너에서 찾아서 넣어준다. 이렇게 객체 의존관계를 외부에서 넣어주는 것을 의존성 주입(DI)라 한다.

 

스프링이 시작될 때, @Controller, @Service, @Repository와 같은 컴포넌트들을 스캔하고 스프링에 빈으로 등록한다. 기본적으로 @Component가 있으면 스프링 빈으로 자동등록 되며, 위의 세 어노테이션도 @Component를 포함하고 있기에 스프링 빈으로 자동 등록 된다. 스프링 빈으로 등록된다면, "스프링 컨테이너에서 빈이 관리된다"라고 말한다. 이후 @Autowired 어노테이션으로 등록된 빈들의 의존 관계를 연결한다.

 

 

 

예를 들어, MemberController의 생성자에 @Autowired를 붙임으로써, 스프링은 스프링이 저장하고 있는 memberSevice 객체를 컨트롤러가 생성될 때 가져와 준다. 즉, 생성자에 @Autowired가 있으면 스프링이 연관된 객체를 스프링 컨테이너에서 찾아서 넣어준다. 이렇게 객체 의존관계를 외부에서 넣어주는 것을 의존성 주입(DI)라 한다.

 

 

의존성 주입의 방법

 

  1. 필드 주입
  2. setter 주입
  3. 생성자 주입 (권장)

 

생성자 주입 권장 이유

 

  1. 순환 의존성 확인: 필드 주입으로는 순환 의존성을 파악하기 어렵다. 생성자 주입을 하게 되면 서버 기동 시 순환 의존성을 가지는 요소들을 파악할 수 있게 에러메시지를 표시하면서 서버 기동이 되지 않는다.
  2. 불변성: 필드 주입은 final을 선언할 수 없지만 생성자 주입은 final을 선언함으로써 객체가 변하지 않도록 방지해준다.
  3. 단일 책임 원칙 위반 확인

 

@Autowired 어노테이션을 이용한 의존성 주입

 

객체 연결을 할 때 사용. 필드 혹은 생성자에 사용하면 컨트롤러, 서비스 등이 생성될 때 스프링빈에 등록되어 있는 객체를 가져다 넣어준다.

 

1. 필드 주입 방법

public  class  ExampleCase{
 
    @Autowired
    private  ChocolateService  chocolateService;
 
    @Autowired
    private  DrinkService  drinkService;
}

 

2. 생성자 주입 방법 (권장)

public  class  ExampleCase{
 
    private  final  ChocolateService  chocolateService;
    private  final  DrinkService  drinkService;
 
    @Autowired
    public  ExampleCase(ChocolateService  chocolateService, DrinkService  drinkService){
        this.chocolateService = chocolateService;
        this.drinkService = drinkService;
    }
}

 

 

@RequiredArgsConstructor 어노테이션을 이용한 의존성 주입

 

@RequiredArgsConstructor // final로 선언된 멤버 변수를 자동으로 생성합니다.
@RestController // JSON으로 데이터를 주고받음을 선언합니다.
public class ProductRestController {

    private final ProductService productService;
    private final ProductRepository productRepository;

    // 등록된 전체 상품 목록 조회
    @GetMapping("/api/products")
    public List<Product> getProducts() {
        return productRepository.findAll();
    }
}

@RequiredArgsConstructor라는 어노테이션을 붙이면 final 필드나 @NonNull이 붙은 필드에 대해 생성자를 생성해 준다. 주로 의존성 주입의 편의성을 위해서 사용된다.

 

어떠한 빈(Bean)에 생성자가 오직 하나만 있고, 생성자의 파라미터 타입이 빈으로 등록 가능한 존재라면 이 빈은 @Autowired 어노테이션 없이도 의존성 주입이 가능하다.

 

 

 

3. AOP(관점지향 프로그래밍) 지원

 

관심사의 분리를 아주 스마트하고 깔끔하게 처리해준다.

 

스프링은 AOP를 통해 반복적인 코드를 줄이고 개발자가 핵심 비즈니스 로직에만 집중할 수 있도록 지원한다.

 

" 공통 관심 사항 (cross-cutting concern) vs 핵심 관심 사항 (core concern) "

 

비즈니스 로직은 아니지만 보안, 로그, 트랜잭션과 같이 반드시 처리가 필요한 부분을 스프링에서는 공통 관심사항이라고 한다. 예를 들어, 회원가입과 회원 조회에서 시간을 측정하는 기능은 핵심 관심 사항이 아니다. 여기서 시간을 측정하는 기능은 공통 관심 사항이다. 이들을 같은 Class 안에 메소드로 구현하면 핵심 비즈니스 로직과 공통 관심 사항에 해당하는 로직이 섞여서 유지보수가 매우 어렵다. 하지만 시간을 측정하는 로직은 별도의 공통 로직으로 만들기도 매우 어렵다. 이 경우 시간을 측정하는 로직을 변경할 때에는 모든 로직을 찾아가며 변경해야 하는 불상사가 생긴다.

 

 

위의 그림과 같이 모든 Class에 시간 측정 로직을 일일히 작성해야 했지만, AOP를 사용해 아래의 그림처럼 한 번에 해결할 수 있다.

 

@Component
@Aspect
public class TimeTraceAop {

    @Around("execution(* hello.hellospring..*(..))")

    public Object execute(ProceedingJoinPoint joinPoint) throws Throwable {
        long start = System.currentTimeMillis();
        System.out.println("START: " + joinPoint.toString());
        try {
            return joinPoint.proceed();
        } finally {
            long finish = System.currentTimeMillis();
            long timeMs = finish - start;
            System.out.println("END: " + joinPoint.toString()+ " " + timeMs +
                    "ms");
        }
    }
}

AOP로 활용할 객체에는 @Aspect 어노테이션을 붙여줘야 한다.

 

@Around로 AOP를 적용할 객체를 지정한다. 문법을 익히면 반복되는 패턴이라 단순하다. 위는 hello.hellospring 하위 패키지에 모두 AOP 로직을 적용하라는 의미의 예시코드이다.

 

자, 이제 위의 문제가 해결되었다. 회원가입과 회원 조회와 같은 핵심 관심사항과 시간을 측정하는 공통 관심 사항을 분리하였다. 시간을 측정하는 로직을 별도의 공통 로직으로 만들었다. 이제 핵심 관심 사항을 깔끔하게 유지할 수 있다. 공통 관심사항 로직을 변경할 일이 생기면 AOP로 사용하는 로직의 코드만 바꾸면 된다. 심지어, 원하는 적용 대상을 선별해 적용할 수도 있게 되었다!

 

 

 

4. 편리한 MVC 구조

 

MVC = Model, View, Controller

 

요새는 Controller와 View를 쪼개는 것이 기본이다.

View는 화면에 관련된 일만! Controller는 서버딴에 관련된 일만 준다!

 

  • Model
    • 어플리케이션의 데이터이며, 모든 데이터 정보를 가공하여 가지고 있는 컴포넌트이다.
    • 사용자가 이용하려는 모든 데이터를 가지고 있어야 한다.
    • View 또는 Controller에 대해 어떤 정보도 알 수 없어야 한다.
  • View
    • 시각적인 UI 요소를 지칭하는 용어이다.
    • 모델이 가지고 있는 데이터를 저장하면 안된다.
    • 모델이나 컨트롤러에 대한 정보를 알면 안되며, 단순 표시 역할만 가지고 있다.
  • Controller
    • 모델과 뷰를 연결해주는 역할을 한다.
    • 모델과 뷰에 대한 정보를 알아야 한다.
    • 모델과 뷰의 변경을 인지하여 대처해야 한다.

 

 

 

5. WAS에 독립적인 개발 환경

 

웹서버는 정적인 데이터를 처리하는 서버로 단순 이미지 HTML을 처리하는 서버이며, WAS(Web Application Server)는 동적인 데이터를 처리하는 서버로 DB 연동 데이터 조작등과 같은 처리를 WAS에서 한다.

 

스프링 Boot 기본 내장 WAS는 Apache Tomcat이다.

 

@SpringBootApplication을 실행하면 자동으로 웹 서버가 실행된다. 알아두어야 할 점은, 스프링 부트가 웹 서버 자체인 것은 아니다. 스프링 부트는 스프링을 편하게 사용하기 위한 툴에 불과하다. 웹 서버가 실행된다함은, 스프링 부트가 내장된 웹 서버를 자동으로 구동시킨다는 뜻이다.

 

스프링부트의 내장 WAS를 꼭 사용해야 하는 것은 아니고, 자유롭게 변경해 사용할 수 있다

 

 

 

마치며,

결국, 위에서 설명한 spring이 제공하는 기능이 프로그래머로 하여금 편리하게 개발할 수 있도록 한다. 비즈니스 로직에만 집중할 수 있게 해주는 것이다. 모든 상황에서 필요한 기본적인 코드들은 이미 다 짜여 있다. 프로그래머는 해당 상황에 최적화 시키기 위한 비즈니스 로직과 관련한 변수와 메소드를 개발하는 데 집중할 수 있다.

 

추가로, 스프링은 개발자가 기본적인 디자인 패턴(DI, AOP, 서비스 추상화) 등을 강제적으로 사용하게끔 함으로서 코드 구조 퀄리티의 최소한을 보장하는 장점 역시 있다. 

 

 

 

참고자료 및 출처:

스프링프레임워크: https://kimvampa.tistory.com/35

스프링프레임워크: https://freestrokes.tistory.com/79

코드로 배우는 스프링부트: https://www.inflearn.com/course/스프링-입문-스프링부트/lecture/49577?tab=note&speed=1.25

면접 관련 스프링 키워드: https://alswns1201.medium.com/면접-대비-및-java-관련-이론-정리-6e3f0bbe0fd5

스프링의 장단점: https://okky.kr/article/225553

@Autowired: https://firework-ham.tistory.com/28

@RequiredArgsConstructor: https://webdevtechblog.com/requiredargsconstructor-를-이용한-의존성-주입-dependency-injection-4f1b0ac33561

스프링과 빈의 의존관계: https://no-delay-code.tistory.com/61

MVC: https://iri-kang.tistory.com/4