스프링부트

스프링부트를 쓰는 이유

스프링부트는 스프링 프레임워크를 기반으로 한 자바 웹 어플리케이션을 보다 쉽게 개발하고 운영하기 위해 만들어졌습니다. 다음과 같은 이유로 스프링부트를 쓰는 것이 유용합니다.

1. 빠른 개발

스프링부트는 설정이 간단하고, 빠른 애플리케이션 개발을 지원합니다. 개발자가 직접 설정을 할 필요 없이, 스프링부트의 자동설정을 이용하여 필요한 의존성을 추가하고 설정을 적용할 수 있습니다.

2. 간편한 배포

스프링부트는 내장된 서버(Tomcat, Jetty 등)를 이용하여 애플리케이션을 실행할 수 있습니다. 이는 개발자가 별도의 외부 서버를 구축하지 않아도 되며, 배포가 간단해집니다.

3. 통합된 모니터링

스프링부트는 애플리케이션의 상태를 모니터링할 수 있는 엔드포인트(Actuator)를 제공합니다. 이 엔드포인트를 이용하여 메모리 사용량, 쓰레드 개수, JDBC 연결 정보 등을 확인할 수 있습니다.

4. 간단한 테스트 환경 구축

스프링부트는 내장된 서버와 자동설정 기능을 이용하여 간단한 테스트 환경을 구축할 수 있습니다. 개발자는 별도의 서버나 DB 설정 없이 애플리케이션을 바로 테스트할 수 있습니다.

5. 다양한 환경 지원

스프링부트는 다양한 환경에서 애플리케이션을 실행할 수 있습니다. 클라우드 서비스(AWS, GCP 등)를 비롯하여, 스프링클라우드와 통합하여 클라우드에서 애플리케이션을 실행할 수 있습니다.

6. 자동설정 기능

스프링부트는 자동설정 기능을 제공하여 설정을 간단하게 처리할 수 있습니다. 예를 들어, JPA를 이용하여 DB 작업을 수행할 경우, 스프링부트는 자동으로 EntityManager를 생성하고, 트랜잭션 처리를 해줍니다.

7. 개발 환경 구축의 용이성

스프링부트는 개발환경을 구축하는데 있어 많은 도움을 줍니다. Maven, Gradle 등의 빌드 툴을 이용하여 의존성을 관리하고, JAR, WAR 파일을 쉽게 빌드할 수 있습니다. 또한 스프링부트는 스프링 이니셜라이저를 제공하여, 초기 설정을 쉽게 생성할 수 있습니다.

8. 관련 커뮤니티

스프링부트는 스프링 프레임워크를 기반으로 하기 때문에, 스프링 프레임워크와 관련된 다양한 커뮤니티를 활용할 수 있습니다. 또한 스프링부트는 다양한 라이브러리와 프레임워크와 연동이 가능합니다.

9. 성능

스프링부트는 내장된 서버를 이용하므로, 서버를 구축하지 않아도 되며, 클라우드에서 애플리케이션을 실행할 수 있어, 성능면에서 우수한 결과를 보입니다. 또한, 스프링부트는 내부적으로 캐싱을 적용하여 성능을 향상시킬 수 있습니다.

10. 다양한 기능

스프링부트는 다양한 기능을 제공합니다. AOP, 캐싱, 보안 등 다양한 모듈을 이용하여 애플리케이션을 구성할 수 있습니다. 또한, 스프링부트는 스프링데이터를 이용하여 다양한 데이터베이스를 지원하며, 스프링 시큐리티를 이용하여 보안기능을 구현할 수 있습니다.

스프링부트는 스프링 프레임워크를 보다 쉽게 사용할 수 있도록 만들어졌습니다. 스프링부트를 이용하면 애플리케이션을 빠르게 개발하고, 배포할 수 있으며, 모니터링과 테스트를 간편하게 수행할 수 있습니다. 또한, 다양한 커뮤니티와 라이브러리를 활용하여 애플리케이션을 보다 높은 수준으로 구현할 수 있습니다. 따라서, 스프링부트는 자바 개발자에게 권장되는 프레임워크 중 하나입니다.

스프링부트는 왜 생겨났을까?

스프링부트는 스프링 프레임워크를 기반으로 한 자바 웹 어플리케이션을 보다 쉽게 개발하고 운영하기 위해 만들어졌습니다. 이전에는 스프링 프레임워크를 이용한 개발 시 많은 설정과 라이브러리가 필요했고, 이를 일일이 구성하는 것이 어려웠습니다. 이러한 문제를 해결하기 위해 스프링부트는 자동설정과 스타터(dependency management)를 제공하여 개발자가 불필요한 설정 및 의존성 관리에 신경쓰지 않고, 개발에 집중할 수 있도록 도와줍니다.

스프링부트의 개발 목적은 다음과 같습니다.

  1. 개발자 생산성 향상
    • 스프링부트는 자동설정, 스타터, CLI(Command Line Interface) 등을 제공하여 개발자가 빠르게 개발할 수 있도록 돕습니다.
  2. 설정 간소화
    • 스프링부트는 설정 파일의 크기를 줄이고, 불필요한 설정을 자동으로 제거하여 개발자가 설정 관리를 쉽게 할 수 있도록 돕습니다.
  3. 내장 서버 제공
    • 스프링부트는 내장된 서버를 제공하여 개발자가 별도의 서버를 구축하지 않아도 애플리케이션을 실행할 수 있습니다.
  4. 확장 가능성
    • 스프링부트는 스프링 프레임워크와 연동하여 스프링의 다양한 모듈과 기능을 활용할 수 있습니다.
  5. 운영 및 배포의 용이성
    • 스프링부트는 애플리케이션의 설정과 라이브러리 의존성을 자동으로 관리하여, 배포 시 일관된 환경을 제공합니다. 또한, 애플리케이션의 상태를 모니터링할 수 있는 엔드포인트를 제공하여, 운영 및 모니터링을 용이하게 합니다.

스프링 DI (Dependency Injection)

DI는 Dependency Injection의 약자로, 객체간의 의존성을 느슨하게 만들기 위한 디자인 패턴 중 하나입니다. 객체가 직접 의존하는 객체를 생성하거나, 컨테이너가 직접 생성하여 의존성을 주입하는 방식으로 작동합니다.

스프링에서는 DI를 위해 @Autowired 어노테이션을 제공합니다. @Autowired 어노테이션을 사용하여 의존성 주입을 자동화할 수 있습니다. 이를 통해 객체 간의 결합도를 낮추고, 유지보수 및 확장성을 높일 수 있습니다.

객체 주입 방식의 종류와 특징

DI (Dependency Injection)는 객체 간의 의존성을 낮추기 위한 디자인 패턴으로, 스프링에서는 다양한 객체 주입 방식을 제공합니다. 여러 객체 주입 방식의 특징과 차이점을 살펴보겠습니다.

1. 생성자 주입(Constructor Injection)

생성자 주입은 객체를 생성할 때, 생성자를 통해 의존성을 주입하는 방식입니다. 생성자 주입 방식은 다음과 같은 특징이 있습니다.

  • 불변성(Immutability): 생성자를 통해 한 번 주입되면 의존성을 변경할 수 없습니다.
  • 필수적인 의존성 주입: 모든 의존성이 생성자에 주입되어야 하기 때문에, 필수적인 의존성이 존재할 경우 사용합니다.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public class UserServiceImpl implements UserService {

    private final UserRepository userRepository;

    public UserServiceImpl(UserRepository userRepository) {
    this.userRepository = userRepository;
    }

    // ...
    }

2. Setter 주입(Setter Injection)

Setter 주입은 Setter 메서드를 통해 의존성을 주입하는 방식입니다. Setter 주입 방식은 다음과 같은 특징이 있습니다.

  • 유연성: Setter 메서드를 통해 의존성을 변경할 수 있습니다.
  • 선택적인 의존성 주입: Setter 메서드가 존재하면 해당 의존성을 주입할 수 있습니다.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public class UserServiceImpl implements UserService {

    private UserRepository userRepository;

    public void setUserRepository(UserRepository userRepository) {
    this.userRepository = userRepository;
    }

    // ...
    }

3. 필드 주입(Field Injection)

필드 주입은 필드를 직접 주입하는 방식입니다. 필드 주입 방식은 다음과 같은 특징이 있습니다.

  • 코드 가독성 저하: 필드를 직접 주입하면, 해당 필드가 어떤 의존성을 가지는지 명확하지 않습니다.
  • 유연성: 필드를 직접 주입하면, 의존성을 변경하기 쉽습니다.
  • 적극적인 의존성 주입: 모든 필드에 대해 의존성을 주입할 수 있습니다.
    1
    2
    3
    4
    5
    6
    7
    public class UserServiceImpl implements UserService {

    @Autowired
    private UserRepository userRepository;

    // ...
    }

4. 메서드 주입(Method Injection)

메서드 주입은 일반적인 메서드를 통해 의존성을 주입하는 방식입니다. 메서드 주입 방식은 다음과 같은 특징이 있습니다.

  • 특정 메서드를 통한 의존성 주입: 일반적인 메서드를 통해 의존성을 주입하기 때문에, 특정 메서드에 대해 유연하게 처리할 수 있습니다.
  • 선택적인 의존성 주입: 의존성이 존재하는 메서드에 대해서만 의존성을 주입할 수 있습니다.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public class UserServiceImpl implements UserService {

    private UserRepository userRepository;

    @Autowired
    public void setUserRepository(UserRepository userRepository) {
    this.userRepository = userRepository;
    }

    // ...
    }
    Setter 주입과 메서드 주입(Method Injection)은 서로 비슷한 방법으로 의존성을 주입하는 방식이지만, 몇 가지 차이점이 있습니다.

Setter 주입은 객체 생성 후, Setter 메서드를 통해 의존성을 주입하는 방식입니다. 이 방식은 의존성이 변경될 가능성이 있는 경우, Setter 메서드를 통해 의존성을 변경할 수 있는 장점이 있습니다.

메서드 주입은 생성자나 Setter 메서드가 아닌, 클래스 내부의 일반적인 메서드를 통해 의존성을 주입하는 방식입니다. 이 방식은 객체 생성 시점에 의존성이 결정되지 않아도 되는 장점이 있으며, Setter 메서드와 달리 메서드 이름에 제약이 없는 유연한 방식입니다.

즉, Setter 주입과 메서드 주입은 서로 다른 방식의 의존성 주입 방법입니다. Setter 주입은 Setter 메서드를 이용하여 의존성을 주입하고, 메서드 주입은 생성자나 Setter 메서드 이외의 일반적인 메서드를 이용하여 의존성을 주입합니다.

객체 주입 방식 선택 시 고려해야 할 사항

객체 주입 방식을 선택할 때 고려해야 할 사항은 다음과 같습니다.

  1. 의존성의 필수/선택 여부: 생성자 주입 방식은 모든 의존성을 필수적으로 주입해야 하기 때문에, 필수적인 의존성이 존재하는 경우에 사용됩니다. 반면에 Setter 주입 방식은 선택적인 의존성이 존재하는 경우에 사용됩니다.

  2. 객체의 불변성: 생성자 주입 방식은 의존성이 주입된 이후에는 의존성을 변경할 수 없습니다. 따라서 불변성이 필요한 객체에는 생성자 주입 방식이 적합합니다.

  3. 유연성: Setter 주입 방식은 Setter 메서드를 통해 의존성을 변경할 수 있기 때문에, 의존성 변경이 빈번하게 발생하는 경우에 적합합니다.

  4. 코드 가독성: 필드 주입 방식은 필드를 직접 주입하기 때문에, 코드 가독성이 저하될 수 있습니다. 따라서 코드 가독성이 중요한 경우에는 생성자 주입 방식이나 Setter 주입 방식을 사용하는 것이 좋습니다.

  5. 특정 메서드를 통한 의존성 주입: 메서드 주입 방식은 특정 메서드를 통해 의존성을 주입하기 때문에, 특정 메서드에 대해 유연하게 처리할 수 있습니다.

스프링 IOC (Inversion of Control)

스프링의 핵심 컨테이너인 ApplicationContext는 Inversion of Control(IoC)을 구현한 것입니다. IoC는 객체 지향 프로그래밍에서 발생하는 의존성 관리 문제를 해결하기 위해 등장한 개념으로, 객체 간의 의존성을 자동으로 연결해주는 프로그래밍 방식입니다.

스프링의 IoC는 객체의 생성, 소멸 등의 라이프사이클을 관리하며, 객체 간의 의존성을 관리하는 데에 중점을 둡니다. IoC는 객체 간의 의존성을 자동으로 연결해주는 역할을 합니다.

스프링의 IoC는 객체를 생성하는 시점과 의존성을 주입하는 시점을 분리합니다. 이를 통해 객체의 라이프사이클을 유연하게 관리할 수 있습니다. 스프링의 IoC는 객체 간의 의존성을 자동으로 주입해주는데, 이를 의존성 주입(Dependency Injection, DI)이라고 합니다.

스프링의 IoC는 빈(Bean)이라는 단위로 객체를 관리합니다. 빈은 ApplicationContext에 등록되어 있으며, 다른 빈에서 의존성 주입을 받을 수 있습니다. 빈은 XML 설정 파일이나 어노테이션을 이용하여 등록할 수 있습니다.

스프링의 IoC는 다양한 방식으로 의존성 주입을 처리합니다. 주요한 의존성 주입 방식으로는 생성자 주입(Constructor Injection), Setter 주입(Setter Injection), 필드 주입(Field Injection) 등이 있습니다. 이들은 각각 객체 생성 시점에 의존성을 주입하는 방식이 다르며, 상황에 따라 선택해서 사용할 수 있습니다.

스프링의 IoC는 객체의 생성과 소멸, 의존성 주입 등을 자동으로 처리해주므로, 객체 간의 의존성 관리 문제를 해결할 수 있습니다. 이를 통해 코드의 재사용성과 유지보수성을 높일 수 있습니다.

스프링 AOP

AOP(Aspect-Oriented Programming)는 객체 지향 프로그래밍의 한계를 보완하기 위해 등장한 프로그래밍 패러다임입니다. AOP는 애플리케이션의 핵심 로직에서 발생하는 부가적인 작업들을 모듈화하여 코드의 재사용성과 유지보수성을 높여줍니다.

스프링 AOP는 스프링 프레임워크에서 제공하는 AOP 구현체입니다. 스프링 AOP는 프록시 패턴을 이용하여 메서드 실행 전/후 등 특정 시점에 부가적인 로직을 수행할 수 있습니다.

스프링 AOP의 주요 구성 요소는 다음과 같습니다.

  1. Pointcut
  • Advice가 적용될 메서드를 지정하는 것을 의미합니다.
  1. Advice
  • 메서드 실행 전/후 등 특정 시점에서 수행할 로직을 정의합니다.
  1. Joinpoint
  • Advice가 적용될 수 있는 위치를 의미합니다. 메서드 호출, 예외 발생 등이 Joinpoint에 해당합니다.
  1. Aspect
  • Pointcut과 Advice의 결합체를 의미합니다.
  1. Target
  • AOP가 적용되는 대상 객체를 의미합니다.

스프링 AOP는 다양한 Advice를 지원합니다.

  1. Before
  • 메서드 실행 전에 수행할 로직을 정의합니다.
  1. After returning
  • 메서드 실행 후 리턴 값이 존재할 경우 수행할 로직을 정의합니다.
  1. After throwing
  • 메서드 실행 중 예외가 발생한 경우 수행할 로직을 정의합니다.
  1. After
  • 메서드 실행 후 수행할 로직을 정의합니다.
  1. Around
  • 메서드 실행 전/후 등 모든 시점에서 수행할 로직을 정의합니다.

스프링 AOP는 자바의 Proxy 클래스를 이용하여 프록시 객체를 생성합니다. 이를 통해 타깃 객체의 메서드를 호출하기 전/후에 부가적인 로직을 수행할 수 있습니다. 또한, AOP를 적용할 때 XML 설정 파일이나 어노테이션을 이용하여 간편하게 설정할 수 있습니다.

스프링 AOP를 사용하면 애플리케이션의 핵심 로직과 부가적인 로직을 분리하여 구현할 수 있습니다. 이를 통해 코드의 재사용성과 유지보수성을 높일 수 있으며, 특히 로깅, 트랜잭션, 보안 등과 같은 공통적인 작업을 간편하게 처리할 수 있습니다.

스프링 ORM (Object-Relational Mapping)

ORM은 Object-Relational Mapping의 약자로, 객체와 관계형 데이터베이스 간의 매핑을 자동화하는 기술입니다. 스프링에서는 JPA(Java Persistence API)를 이용하여 ORM을 구현합니다.

스프링에서 ORM을 이용하면 객체지향적인 방식으로 데이터를 다룰 수 있습니다. 또한, ORM을 이용하면 SQL을 직접 작성하는 것보다 더 간편하게 데이터를 다룰 수 있습니다. 스프링에서는 Hibernate, MyBatis 등 다양한 ORM 프레임워크를 지원합니다.

Hibernate

Hibernate는 자바 언어를 위한 ORM(Object-Relational Mapping) 프레임워크입니다. ORM은 객체와 관계형 데이터베이스 간의 매핑을 자동으로 처리하는 기술입니다. 이를 통해 SQL 쿼리를 직접 작성하지 않아도 데이터베이스와 상호작용할 수 있습니다.

Hibernate는 객체와 데이터베이스 간의 매핑을 위해 XML 또는 어노테이션을 사용할 수 있습니다. Hibernate는 객체를 데이터베이스 테이블에 매핑하는 방식으로 동작합니다. 이를 위해 개발자는 데이터베이스 스키마와 매핑되는 Java 클래스를 작성하고, Hibernate 설정 파일을 작성하여 데이터베이스 연결 정보를 설정합니다.

Hibernate는 자바 Persistence API(JPA)를 구현하는 ORM 프레임워크입니다. JPA는 Java에서 ORM을 구현하기 위한 표준 인터페이스입니다. Hibernate는 JPA의 구현체 중 하나로, JPA에서 정의한 인터페이스를 구현하여 ORM을 제공합니다.

Hibernate는 데이터베이스와의 상호작용을 위해 Session과 Transaction 개념을 사용합니다. Session은 데이터베이스와의 세션을 의미하며, 데이터베이스 연결과 동일한 개념입니다. Transaction은 데이터베이스에서 수행되는 일련의 작업을 의미하며, 일반적으로 Session 내에서 실행됩니다.

Hibernate의 주요 기능으로는 다음과 같은 것들이 있습니다.

객체와 데이터베이스 간의 매핑 처리
객체와 데이터베이스 간의 관계 처리
객체와 데이터베이스 간의 쿼리 처리
객체와 데이터베이스 간의 트랜잭션 처리
Hibernate는 자바에서 ORM을 구현하는 대표적인 프레임워크 중 하나입니다. JPA를 구현한 ORM 프레임워크 중에서도 가장 널리 사용되는 프레임워크 중 하나이며, 대규모 애플리케이션에서도 안정적으로 사용됩니다.

MyBatis 와 Hibernate

MyBatis와 Hibernate은 모두 자바에서 사용되는 ORM 프레임워크입니다. 하지만, MyBatis는 Hibernate과는 다른 방식으로 동작합니다.

MyBatis는 SQL 매핑 기반의 ORM 프레임워크로, 개발자가 직접 SQL 쿼리를 작성하고, 데이터베이스와 상호작용할 수 있는 방식으로 동작합니다. MyBatis는 SQL 매핑 파일과 Java 인터페이스를 이용하여 데이터베이스와의 상호작용을 처리합니다. 이에 비해 Hibernate는 객체와 데이터베이스 간의 매핑을 처리하며, SQL 쿼리를 자동으로 생성합니다.

MyBatis는 JDBC를 이용하여 데이터베이스와 상호작용하며, SQL 쿼리를 직접 작성할 수 있습니다. 반면, Hibernate는 JDBC보다 더 높은 추상화 수준을 제공하며, 객체와 데이터베이스 간의 매핑을 자동으로 처리합니다.

따라서, MyBatis는 Hibernate과는 다른 방식으로 동작합니다. MyBatis에서는 Hibernate과 같은 ORM 프레임워크에서 제공하는 객체와 데이터베이스 간의 매핑 기능이 제한적이며, 대신 SQL 쿼리를 직접 작성하여 데이터베이스와 상호작용하는 방식으로 동작합니다.

스프링 MVC life cycle

스프링 MVC의 라이프사이클은 크게 다음과 같은 단계로 구성됩니다.

요청 처리 과정 시작

HandlerMapping을 통한 핸들러 검색

HandlerAdapter를 통한 핸들러 실행

ViewResolver를 통한 뷰 검색

ModelAndView를 이용한 뷰 실행

요청 처리 과정 종료

요청 처리 과정 시작
사용자가 HTTP 요청을 서버에 전송하면, 해당 요청은 DispatcherServlet에 의해 처리됩니다. DispatcherServlet은 스프링 컨테이너에 등록된 모든 빈을 검색하며, HandlerMapping, HandlerAdapter, ViewResolver 등의 빈을 찾아 사용합니다.

HandlerMapping을 통한 핸들러 검색
DispatcherServlet은 HandlerMapping 빈을 찾아 핸들러를 검색합니다. HandlerMapping은 요청 URI를 이용하여 적절한 핸들러를 검색하며, 해당 핸들러가 없을 경우 404 에러를 반환합니다.

HandlerAdapter를 통한 핸들러 실행
HandlerMapping을 통해 검색된 핸들러는 HandlerAdapter를 이용하여 실행됩니다. HandlerAdapter는 검색된 핸들러가 처리할 수 있는 형태로 요청을 변환하고, 핸들러가 처리한 결과를 ModelAndView 객체에 담아 반환합니다.

ViewResolver를 통한 뷰 검색
ModelAndView 객체는 DispatcherServlet에 반환됩니다. DispatcherServlet은 ViewResolver 빈을 이용하여 뷰를 검색합니다. ViewResolver는 검색된 뷰의 위치와 형식을 찾아 반환합니다.

ModelAndView를 이용한 뷰 실행
검색된 뷰는 DispatcherServlet에 의해 실행됩니다. ModelAndView 객체에 담겨있는 데이터를 이용하여 뷰가 생성되며, HTML, JSP, JSON 등의 형식으로 응답됩니다.

요청 처리 과정 종료
뷰가 실행된 후, DispatcherServlet은 요청 처리 과정을 종료합니다. 이때, 요청과 응답 객체는 Servlet Container에 반환됩니다.

스프링 MVC의 라이프사이클은 요청 처리에 필요한 여러 개의 빈들을 순서에 맞게 실행하며, 요청과 응답 객체를 생성 및 관리합니다. 이를 통해 사용자 요청에 대한 적절한 응답을 반환할 수 있습니다.