Spring을 사용하기 전에 알아야 할 개념들
기본적인 개념을 알아두면 Spring을 더 잘 사용할 수 있을 거라고 생각합니다.
내일배움캠프 Kotlin & Spring 입문 강의 자료를 기반으로 정리했습니다.
Framework 프레임워크
Application을 개발하기 위한 규약과 다양한 요소들을 제공하는 틀
Library 라이브러리
Application 개발 시 활용 가능한 도구(코드)의 집합
Framework와 Library의 차이
Framework는 Application을 호출하는 Caller 역할, Library는 Application이 호출하는 Callee 역할.
Framework는 개발자가 Application 관련 코드를 작성하면 이를 알아서 호출해주고, Library는 우리가 Application 코드를 작성할 때 활용하는 도구
Spring 스프링
Java 기반 Web Application을 만드는 데 특화된 Framework.
개발자가 비즈니스 로직에 집중할 수 있도록 Enterprise Application의 Plumbing에 중점을 둡니다. Application 레벨의 인프라를 지원합니다.
Plumbing
Application의 각 요소들을 조합하는 과정.
Spring Framework에서는 이 과정을 개발자들이 손수 코드를 통해 표기해줘야 합니다.
Spring Boot 스프링 부트
개발자들이 직접 코드로 표기하지 않고도 자동으로 조합이 이루어질 수 있도록 도와주는 Spring의 Extension.
SSR (Server Side Rendering), CSR (Client Side Rendering)
아래 글 참고
https://adorecamus.tistory.com/entry/SSR%EA%B3%BC-CSR-%EA%B7%B8%EB%A6%AC%EA%B3%A0-Spring
Web application을 만들기 위한 요구사항
- 사용자와 프론트엔드의 요청을 처리하기 위한 적절한 응답
- 예외 처리, 예외 발생 시 적절한 응답
- 인증과 인가
- 비즈니스 로직 처리
- Transaction 관리
- 스토리지 및 다른 외부 시스템과 통신
인증 Authentication
사용자의 신원 파악. 예) 은행 사이트 로그인
인가 Authorization
인증된 사람에 한해 해당 리소스에 접근할 권한이 있는지 확인. 예) 은행 계좌 송금 - 해당 사용자의 계좌인지 확인
Transaction 트랜잭션
데이터베이스와 같은 지속적인 데이터 저장소에서 수행되는 작업의 논리적인 실행 단위.
예) 은행 계좌 송금 시 1. 보내는 사람의 계좌에서 돈이 빠져나가고, 2. 받는 사람의 계좌에 돈이 들어오는 과정 모두 성공하거나 모두 실패해야 합니다.
아래에서 다시 설명하겠습니다.
Spring의 Layer 구조 (계층 구조)
- Web Layer
- Service Layer
- Repository Layer
Web Layer 웹 레이어
Application의 최상위 Layer.
클라이언트의 요청을 받고 응답을 주는 역할 - Controller
하위 Layer에서 발생한 예외들을 처리 - Exception Handler
인증과 인가 - Filter
Service Layer 서비스 레이어
Web Layer의 하위 Layer.
Transaction 경계의 역할.
Application Service - 비즈니스 로직 처리 담당. 응답을 Web Layer에 전달.
Infrastructure Service - 데이터베이스, 이메일 서버와 같은 외부 서비스와 통신 담당. Application Service에서 호출해서 사용.
Repository Layer 레포지토리 레이어
가장 하위 Layer.
데이터베이스와 직접 통신하는 역할.
Layer 사이의 인터페이스
- DTO
- Domain Model
DTO (Data Transfer Object)
데이터를 담는 객체.
Kotlin에서는 get, set, equals, hashcode 등의 메서드가 자동으로 구현되는 data class를 주로 이용합니다.
Web Layer와 Service Layer 사이의 데이터를 전달하는 데 쓰입니다. 응답과 요청을 표현합니다.
Domain Model 도메인 모델
Domain Service, Entity, VO(Value Object)를 포함하는 개념.
Service Layer와 Repository Layer 사이에서 쓰입니다. 비즈니스 로직의 민감한 정보가 노출되지 않도록 캡슐화 encapsulate 합니다.
Entity 엔티티 - Database에 저장되는 데이터를 표현한 객체. Entity 클래스는 table과 대응되고, Entity의 Instance는 table의 한 row와 대응됩니다.
VO(Value Object) - 값을 표현하는 객체. Entity의 Property 프로퍼티로, Entity와 Lifecycle을 함께합니다.
Domain Service 도메인 서비스 - Entity가 해결하지 못하는 도메인에 대한 특정 로직 수행. Stateless(상태 없음).
Layer 사이의 전달 과정
- Web Layer가 클라이언트에게 DTO를 요청으로 받습니다.
- Service Layer가 Web Layer로부터 DTO 혹은 Basic Type(Int, String...)을 넘겨받고, 내부에서 로직을 수행하기 위해 Domain Model을 사용합니다.
- Repository Layer가 Entity와 Basic Type을 파라미터로 받고, Entity를 응답합니다.
- Service Layer가 DTO를 Web Layer에 응답합니다.
- Web Layer가 클라이언트에게 DTO를 응답합니다.
(캡슐화의 관점에서 벗어나지만 Domain Model이 Web Layer까지 올라오는 경우도 있습니다.)
Spring의 요청 전달 과정
- Client가 Dispatch Servlet에 HTTP Request를 보냅니다.
- Dispatch Servlet은 HandlerMapping을 통해 요청에 대응되는 Controller를 검색하고 응답을 받습니다.
- Handler Adapter를 통해 Controller에 Request에 대한 처리를 요청합니다.
- Controller, Service, Repository 를 거쳐 비즈니스 로직을 수행한 후 Response를 응답합니다.
- 다시 Handler Adapter가 응답을 Dispatch Servlet에 전달합니다.
- 최종적으로 Dispatch Servlet이 HTTP Response를 Client에게 전달합니다.
HTTP Request
Start line 시작 줄 에 HTTP Method (GET, POST, PUT, PATCH, DELETE) 와 URL 등을 포함합니다.
Header 헤더 를 포함합니다.
Body 본문 은 데이터를 보내는 경우(POST, PUT, PATCH)에만 포함합니다.
HTTP Response
Status line 상태 줄 에 Status code 상태 코드 와 Status text 상태 텍스트 등을 포함합니다.
Header 를 포함합니다.
Body 는 데이터를 보내는 경우에만 포함합니다.
자세한 설명은 https://developer.mozilla.org/ko/docs/Web/HTTP/Messages 참고하세요ㅎㅎ
DI (Dependency Injection) 의존성 주입
객체 내에서 필요로 하는 의존성을 직접 생성하는 게 아니라 외부에서 주입 받아 객체 간 결합도를 낮추는 디자인 패턴.
- 내부에서 의존성 생성
class Dependent() {
private val dependency = Dependency(arg1, arg2)
}
클래스 내부에서 하위 클래스 인스턴스의 lifecycle을 관리합니다.
- Field 기반 의존성 주입
class Dependent() {
lateinit var dependency: Dependency
}
필요한 클래스를 변수로 선언하고 지연초기화 (lateinit) 합니다.
- Setter 기반 의존성 주입
class Dependent() {
private lateinit var dependency: Dependency
fun setDependency(dependency: Dependency) {
this.dependency = dependency
}
}
필요한 클래스를 set 함수를 이용해 초기화합니다.
- Constructor 기반 의존성 주입
class Dependent(private val dependency: Dependency)
인스턴스를 생성할 때 필요한 클래스를 초기화합니다.
private val 로 선언되어 의존되는 객체의 불변성을 확보할 수 있습니다.
순환 참조를 방지합니다.
테스트 코드 작성이 용이합니다.
IoC (Inversion of Contorl) 제어의 역전
객체의 생성과 생명 주기를 외부에서 제어하는 디자인 패턴.
개발자가 구성 요소를 등록하면 Framework가 이를 알아서 호출하고 관리하는 것이 예입니다.
의존성 주입을 우리가 하는 게 아니라 Framework가 해주기 때문에 DI도 IoC의 하나입니다. (Framework를 이용하지 않는 Pure DI 도 있다고 합니다.)
그래서 Framework가 제공하는 IoC 혹은 Framework 자체를 IoC Container라고 부르고, Spring은 DI를 주로 사용하므로 DI Container라고 부르기도 합니다.
개발자는 클래스를 어떻게 초기화해서 주입할지 신경쓰지 않고 Spring에 맡길 수 있습니다.
Annotation 어노테이션
프로그램 코드에 부가적인 정보를 제공해줍니다. @ 로 시작합니다.
Spring Bean 스프링 빈
Spring IoC Container가 관리하는 객체.
@Component Annotation을 통해 등록하거나
@Component
class Dependency()
@Configuration 과 @Bean 을 통해 등록할 수 있습니다.
Application에서 전역적으로 사용하는 설정은 @Configuration을 이용합니다.
Spring이 Bean을 등록할 때 가장 먼저 읽습니다.
@Configuration
class DependencyConfiguration() {
@Bean
fun exampleDependency(): Dependency {
return Dependency()
}
}
(Dependency 클래스를 bean으로 정의하는 configuration)
Singleton 싱글톤
클래스의 인스턴스가 하나만 생성되도록 보장하는 디자인 패턴.
Bean Scope 빈 스코프
Bean 인스턴스의 생명 주기.
기본적으로 Singleton으로 설정됩니다. 이 경우 Bean은 IoC Container당 하나만 생성되고, IoC와 생명 주기를 같이합니다.
이미 만들어진 인스턴스를 재사용하기 때문에 성능이 향상되고, 메모리 자원을 효율적으로 사용할 수 있습니다.
(Singleton 이외의 생명주기도 @Scope 어노테이션을 통해 개발자가 직접 설정할 수 있습니다.)
주의할 점은 Stateless(상태 정보 없음. 반대는 Stateful) 방식으로 생성해야 합니다.
여러 스레드에서 한 인스턴스의 상태 변경을 시도하면 예측하지 못한 상태로 변할 수 있기 때문입니다.
Transaction 트랜잭션
@Transactional 어노테이션으로 쉽게 트랜잭션을 관리할 수 있습니다.
메소드 위에 달아 해당 메소드에서 이루어지는 로직이 모두 성공하거나 실패하도록 보장합니다. 클래스 위에 다는 경우 모든 메소드에 적용됩니다.
트랜잭션 관리는 보통 Service Layer 에서 합니다. Controller에서 여러 Service를 사용하는 경우에는 모든 Service가 성공하거나 실패해야 하기 때문입니다.