개요
프로젝트 개발 전, 우리는 시스템을 구성하는 주요 컴포넌트들(클라이언트와 서버, DB, 외부 서비스 등)을 어떻게 배치하고 통신할지, 어떤 기술을 사용할지에 대한 전체 IT 시스템이 어떻게 구성되고 연동되는지를 다룬다.
이를 시스템 레벨 아키텍처라고 한다.
ex) React로 구성된 프론트엔드 → Spring Boot 백엔드 → MySQL 데이터베이스 → Redis 캐시 → Elasticsearch 검색 엔진
해당 아키텍처 구성이 끝났을 때, 다음으로 결정할 사항은 백엔드 애플리케이션의 내부 구조를 다루는 것이다. 토비의 스프링에서는 스프링 웹 애플리케이션의 아키텍처를 어떻게 설계할 수 있는지 알아볼 것이다.
애플리케이션 아키텍처의 종류
우선 애플리케이션 아키텍처의 종류에는 세 가지로 나눈다.
1.계층형
2.애플리케이션 정보
3.오브젝트 중심
계층형 아키텍처
관심사 분리 원칙에 따른 것으로, 오브젝트 레벨에서의 유연한 설계와 구현 전략을 기억하자. 성격이 다른 코드를 두 개의 오브젝트로 분리하고, 그 사이에 유연한 결합을 가질 수 있도록 인터페이스를 두고, DI 컨테이너를 둬서 오브젝트끼리 직접적 관계를 알지 못하도록 만들었다. 이 원리를 아키텍처 레벨로 확장시킨 것이 계층형 아키텍처이다.
전통적인 MVC 패턴이 쓰일 때 해당 아키텍처 설계가 쓰인다. 가장 널리 사용되는 아키텍처 패턴라고 할 수 있다.
보통 웹 기반의 엔터프라이즈 애플리케이션은 수직적인 세 개의 계층을 갖는다고 해서 3계층 애플리케이션이라고도 한다. 물론, 반드시 3계층이지 않고 좀 더 세분화하는 방식도 있으나, 전형적인 웹 엔터프라이즈 애플리케이션은 책임과 성격으로 보자면 우선적으로 3단계의 논리적인 분류가 가능하다.
각 분류는 다양한 이름들이 있다.
- 프레젠테이션 계층: 웹/UI/MVC 계층
- 비즈니스 계층: 서비스/매니저 계층
- 데이터 액세스 계층: DAO/EIS 계층
1. 데이터 액세스 계층
DAO 패턴을 보편적으로 사용하므로 DAO 계층이라고도 불리며, 이 계층은 DB 외에도 레거시, ERP, 메인프레임 등에 접근하는 역할을 해서 EIS(Enterprise Information System) 계층이라고도 하지만, 대개는 장기적 데이터를 저장하는데에 목적으로 DB 이용이 주된 책임을 갖는다. 또한 외부 시스템을 호출하여 시스템을 이용하는 것은 기반 계층으로 따로 분류하기도 하므로, 토비의 스프링 책에서는 데이터 액세스 계층이라고 분류한다.
데이터 액세스 계층은 그 안에서 다시 세분화된다.
보이는 것처럼 JdbcTemplate가 있어서 DAO 코드에서는 로우레벨의 기반 계층에 존재하는 JDBC와 드라이버, 트랜잭션 동기화 기능을 수작업하지 않아도 된다.(불가는 아님!)
예제로 본다면 다음과 같다.
//직접 JDBC 사용한 데이터 접근 예제
public class JdbcExample {
public static void main(String[] args) {
String url = "jdbc:mysql://localhost:3306/demo";
String username = "root";
String password = "1234";
try (Connection connection = DriverManager.getConnection(url, username, password)) {
String sql = "SELECT * FROM users";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
ResultSet resultSet = preparedStatement.executeQuery();
while (resultSet.next()) {
System.out.println("User: " + resultSet.getString("username"));
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
//JdbcTemplate 사용한 데이터 접근법^^
@Repository
public class UserDao {
private final JdbcTemplate jdbcTemplate;
@Autowired
public UserDao(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
public List<String> getAllUsernames() {
String sql = "SELECT username FROM users";
return jdbcTemplate.query(
sql,
(resultSet, rowNum) -> resultSet.getString("username")
);
}
}
JdbcTemplate는 스프링이 제공하는 추상화 계층이다. 이 추상화 계층은 얼마든지 추가할 수 있다.
2. 서비스 계층
잘 만들어진 서비스 계층 클래스는 이상적인 POJO로 작성된다. 객체지향적 설계로 비즈니스 로직의 핵심을 잘 담아내고, 이를 쉽게 테스트하고 유연한 확장성을 갖기 때문이다.
서비스 계층은 데이터 액세스를 호출하고 이를 활용해서 만들어진다. 때로는 이 기능 외에도 서버나 시스템 레벨에서 제공하는 기반 서비스를 활용할 필요도 있다. (ex. 메일, 메시징 서비스) 한편, 이런 기반 서비스는 3계층 모든 전역에서 접근 가능하도록 할수도 혹은 서비스 계층에서만 접근하도록 제한할 수 있다. 이것은 코드의 특징과 장단점, 활용 예를 잘 살펴 결정하면 된다.
그림을 보면 서비스 계층과 기반 서비스 계층이 서로를 호출하는 걸 볼 수 있다.
원칙적으로 서비스 계층 코드가 기반 서비스 계층의 구현에 종속되면 안 된다. 서비스 계층의 코드는 추상화된 기반 서비스 인터페이스를 통해서만 접근하도록 만들어 종속성을 제거해야 한다. 혹은 AOP를 통해서 서비스 계층에 코드를 침범하지 않고 부가 기능을 추가하는 방법을 활용 해야 한다.
3. 프레젠테이션 계층
이 계층은 가장 복잡한 계층이다. 웹과 프레젠테이션 기술은 끊임없이 발전하고 진보하고 새로운 모델이 등장하기 때문이다. 그러나 단 한 가지, 클라이언트의 종류와 상관없이 HTTP 프로토콜을 사용하는 서블릿이 바탕이 된다.
한편 프레젠테이션 계층은 클라이언트까지 범위가 확장되기도 한다. 대표적으로 RIA와 SOFEA 아키텍처를 예시로 들 수 있다.
계층형 아키텍처 설계 원칙
각 계층은 응집도가 높으면서 다른 계층과는 낮은 결합도를 유지해야 한다. 자신의 계층의 책임에만 충실해야 한다.
1. 각 계층에 오브젝트를 전달할 때는 특정 계층의 종속되지 않는 단순한 오브젝트를 전달해야 한다.
2. 계층 사이의 호출은 인터페이스를 사용한다.(단지 자바의 interface를 의미하는 것이 아님)
3. 모든 메소드를 public하게 하지 말자. 매우 신중히 노출할 메서드를 고른다.
스프링의 DI는 오브젝트 사이의 관계를 다루므로 계층에는 관여하지 않는다. 하지만 모든 경꼐에는 결국 오브젝트가 존재하고 그 사이의 관계도 오브젝트 대 오브젝트로 정의되기 마련이다. 따라서 스프링의 DI는 계층 사이의 관계에도 적용된다고 볼 수 있다.
결론적으로, DI는 계층을 구분하지 않기 때문에 빈 사이의 의존관계를 만들 때 주의하여야 한다는 것이다.
애플리케이션 정보 아키텍처
다음은 애플리케이션 정보 아키텍처이다. 애플리케이션의 데이터 흐름과 정보 처리를 중심으로 설계하는 아키텍처입니다. 이 아키텍처는 주로 데이터와 비즈니스 로직을 어떻게 설계하고 관리할지를 중점적으로 다룬다.
두 가지 접근 방식이 있다.
1) DB/SQL 중심의 로직 구현 방식
- 데이터베이스 자체가 비즈니스 로직을 주도한다.
- SQL, 저장 프로시저(Stored Procedure), 트리거 등을 사용하여 주요 비즈니스 로직을 구현한다.
장점
- 개발하기 쉽다. 자바 코드를 단지 DB와 웹 화면을 연결해 주는 단순한 인터페이스 도구로 전락시키기 때문이다.
단점
- 변화에 매우 취약하다. 객체지향의 장점이 활용되지 못하고 각 계층의 코드가 긴밀히 연결되어 있기 때문이다.
- SQL이나 저장 프로시저에 담긴 로직은 테스트하기 어렵다.
2) 거대한 서비스 계층 방식
- 비즈니스 로직이 애플리케이션 서비스 계층(Service Layer)에 집중된다.
- 복잡한 비즈니스 로직은 서비스 계층에서 처리되어 점점 뚱쭝한 서비스 계층이 된다.
장점
- 자바 언어의 장점을 활용해 로직을 구현하고 테스트하기 용이해졌다.
- DAO 코드는 여러 비즈니스 로직에서 공유해서 사용할 수 있다. 프레젠테이션 계층의 뷰와 1:1 매핑이 되지 않아도 되기 때문이다.
단점
- 복잡한 데이터 트랜잭션 처리가 어려워질 수 있다.
- 여전히 계층 간 결합도가 크다. 데이터 액세스 계층의 SQL은 필요에 따라 만들어지기 쉽기 때문이다.
- 비슷한 기능의 코드가 중복될 수 있다. 크기가 큰 업무 트랜잭션 단위로 서비스 계층의 메소드가 생성되기 때문이다.
오브젝트 중심 아키텍처
마지막으로 오브젝트 중심 아키텍처이다. Object-Oriented Architecture는 객체 지향 프로그래밍(OOP)의 원칙을 기반으로 설계되었다. 이 아키텍처는 도메인 모델과 객체 간의 협력을 목표로 한다.
특징
- 객체가 상태와 행위를 모두 갖고, 객체 간 메시지를 통해 협력하여 기능을 수행한다.
- 도메인 모델을 오브젝트 모델로 활용한다.
- 애플리케이션 정보와 달리 DAO는 순수히 DB와의 상호작용만을 담당/비즈니스 로직은 도메인 객체가 담당
1) 빈약한 도메인 오브젝트
빈약한 오브젝트란 정보만 담겨 있고 그걸 활용하는 기능은 없는 도메인 오브젝트를 말한다. 물론 자주 쓰이는 패턴이다.
그림과 같이 도메인 오브젝트는 세 계층에 독립적으로 존재하면서 일관된 구조의 정보를 담아 계층 간에 전달하는 데 사용된다.
이 방식의 한계는 거대 서비스 계층 방식과 유사하다.
물론 SQL에 의존적인 데이터 방식보다 훨씬 유연하고 간결하지만 여전히 서비스 계층의 메소드에 대부분의 비즈니스 로직이 들어 있기 때문에 로직의 재사용성이 떨어지고 중복의 문제가 발생하기 쉽다.
그러나 비지니스 로직이 복잡하지 않는 한에서 3계층 구조의 특징을 잘 살리는 유용한 아키텍처다.
2) 풍성한 도메인 오브젝트
도메인 객체가 핵심 비즈니스 로직을 스스로 처리한다.
풍성한 도메인 오브젝트 방식은 보다 서비스 계층의 코드가 간결하다. 비즈니스 로직 코드를 이해하기도 쉽다.
장점으로는 다음이 있다.
- 응집도 향상
- 재사용성
- 유지보수 용이
그러나 단점도 존재한다.
- 복잡도 증가
- 학습 곡선
- 초기 설계 비용
도메인 계층 등장
도메인 계층 방식이란 도메인 오브젝트들이 하나의 독립적인 계층을 이뤄서 서비스 계층과 데이터 액세스 계층의 사이에 존재하게 하는 것이다.
기존 방식과 다른 두 가지 특징이 있다.
1. 도메인의 종속적인 비즈니스 로직 처리는 도메인 계층의 오브젝트 안에서 진행된다.(서비스 계층이 아니다)
2. 도메인 오브젝트가 기존 데이터 액세스 계층이나 기반 계층의 기능을 직접 활용한다. (AOP 활용)
이전 방식보다 도메인 오브젝트에 많은 비즈니스 로직을 담아낸다.
그렇다면 서비스 계층의 역할은 사라진 것일까?
답은 아니다.
서비스 계층은 다음의 상황에서 쓰인다.
- 여러 도메인 오브젝트의 기능을 조합 해서 복잡한 작업을 진행해야 하는 경우.
- 트랜잭션 경계를 설정하는 경우.
구조 그림을 보면 새로운 것이 등장한다. 바로 DTO이다.
도메인 오브젝트의 정보를 밖에서 노출시키고자 할 때 정보 접근을 일부로 제한한다. DTO는 정보 전달만을 목적으로 한다.
DTO와 리포트 쿼리
- 리포트 쿼리의 결과를 DTO라는 단순 자바 빈에 담아 전달한다.
- 리포트 쿼리의 결과는 DB 테이블에 담긴 필드의 내용보다는 그 합계, 평균과 같은 계산값이나 아니면 여러 테이블의 필드를 다양한 방식으로 조합해서 만든다.
- 다른 시스템과 자료를 주고 받기 위해서도 도메인 오브젝트에 담긴 정보를 가공하여 DTO를 보낸다.
스프링 애플리케이션을 위한 아키텍처 설계하기
계층형 아키텍처
프렌젠테이션 계층: Spring MVC
서비스 계층: POJO로 구현, 트랜잭션 AOP 적용
데이터 액세스 계층: JDBC를 비롯해 스프링의 데이터 액세스 전략이 적용된 JPA, 하이버네이트 등
정보 전송 아키텍처
우선 빈약한 오브젝트 방식으로 시작한다. 도메인 오브젝트를 계층 간의 정보 전송을 위해 사용한다. 이를 통해 애플리케이션의 정보를 일관된 형태로 유지한다.
프레젠테이션 계층: 도메인 오브젝트를 직접 사용한다. MVC 아키텍처에서도 모델은 도메인 오브젝트를 그대로 사용한다. 사용자가 입력한 폼의 정보도 도메인 오브젝트로 변환해서 사용한다.
서비스 계층: 비즈니스 로직을 도메인 오브젝트를 사용해서 작성한다.
DAO 계층: 서비스 계층에서 요청을 받거나 결과를 돌려줄때 도메인 오브젝트 형태를 유지하도록 한다.
참고로, 스프링의 철학과 개념을 지향하는 것은 도메인 오브젝트 중심의 아키텍처이다.
도메인 계층을 적용하는 경우 AspectJ를 사용하기 때문에 신중히 결정한다.
상태 관리와 빈스코프
서버기반 애플리케이션은 Stateless하다는 특징이 있는데, 애플리케이션의 상태와 장시간의 작업 정보는 유지되어야 할 것이다.
이를 위해 웹 클라이언트에 URL, 파라미터, 폼 히든 필드, 쿠키 등을 이용해 상태 정보 또는 서버에 저장된 상태정보에 키 값 등을 전달해야 한다.
HTTP 세션과 같은 서블릿 컨테이너가 제공하는 저장공간을 활용하기도 한다.
스프링은 기본적으로 상태가 유지되지 않는 빈과 스코프를 사용하도록 권장한다.
단, 상태 유지 스타일의 애플리케이션을 만들 수도 있다.
서드파티 프레임워크, 라이브러리 적용
스프링은 자바 표준 기술 외에도 오픈소스 프레임워크,라이브러리, 사용 제품 등을 활용할 수 있는데,
이 때 스프링이 지원하는 기술인지 살펴보아야 한다.
1. 해당 기술을 스프링의 DI 패턴을 따라 사용할 수 있는가.
- 프레임워크나 라이브러리의 핵심 클래스가 빈으로 등록하고 사용할 수 있도록 팩토리 빈을 제공한다는 의미다.
- 빈으로 등록할 수 있다면 프로퍼티를 통해 세부 조정을 할 수 있다.
- 또, 스프링이 제공하는 추상화 서비스를 통해 다른 리소스에 투명하게 접근할 수 있다.
- 만약 핵심 API나 class가 만들어져 있지 않은 경우 빈으로 사용될 수 있게 팩토리 빈을 도입 해야 한다.
2. 스프링의 서비스 추상화가 적용되었는가.
- 서비스 추상화를 통해 일관된 접근 방법을 제공하는 것이다.
- 일관된 접근 방법은 서드파티 프레임워크 적용 뿐 아니라 필요에 따라 다른 기술로도 쉽게 교체할 수 있다.
- 유연한 설정과 테스트고 가능하게 한다.
3. 스프링이 지지하는 프로그래밍 모델을 적용하였는가.
- 대표적으로 데이터 액세스 기술에 대한 일관된 예외 적용이 있다.
- 이를 통해, 서비스 계층이 데이터 액세스 계층에 종속되지 않도록 한다.
4. 템플릿/콜백이 지원되는가.
- 템플릿/콜백은 반복적으로 등장하는 코드를 재사용할 수 있도록 하는 해당 패턴은 대표적으로 try/finally/catch가 있다.
- 빈으로 등록해서 빈에서 DI 받아 사용할 수 있다.
예외 변환 시 AOP를 통해 이뤄지기도 한다. 특정 예외가 던져지면 포인트 컷을 만들고 어드바이스에서 예외를 추상화된 런타임 예외로 다시 던진다.
'Group Study (2024-2025) > Spring 심화' 카테고리의 다른 글
[ Spring 심화 ] 9주차 - 스프링이란 무엇인가? (7) | 2024.12.04 |
---|---|
[ Spring 심화 ] 8주차 - AOP(3) (0) | 2024.12.01 |
[ Spring 심화 ] 7주차 - AOP(2) (0) | 2024.12.01 |
[ Spring 심화 ] 6주차 - AOP(1) (0) | 2024.12.01 |
[ Spring 심화 ] 5주차 - 서비스 추상화 (2) | 2024.11.06 |