티스토리 뷰
어플리케이션 아키텍처
- 아키텍처
- 어떤 경계 안에 있는 내부 구성요소들이 어떤 책임을 갖고 있고, 어떤 방식으로 서로 관계를 맺고 동작하는지를 규정하는 것
계층형 아키텍처
- 관심, 책임, 성격, 변하는 이유와 방식이 서로 다른 것들을 분리하고 분리된 각 요소의 응집도는 높이고 서로의 결합도는 낮춰주는 방식
- 책임과 성격이 다른 것을 크게 그룹으로 만들어 분리해두는 것
3계층
- 데이터 엑세스 계층 (DAO)
- DB, ERP, 레거시 시스템, 메인 프레임 등에 접근하는 역할
- 사용 기술에 따라서 혹은 추상화 수준에 따라서 수직 구분 (785p)
- 추상화 계층은 필요하다면 얼마든지 추가 가능
- 서비스 계층
- 비즈니스 로직을 담고 있음
- 잘 만들어진 스프링 어플리케이션의 서비스 계층 클래스는 이상적인 POJO 로 작성됨
- DAO 계층, 서버나 시스템 레벨에서 제공하는 기반 서비스를 호출해서 사용
- 787p 그림 9-16
- 원칙적으로 서비스 계층 코드가 기반 서비스 계층의 구현에 종속되어선 안 됨. 즉, 서비스 계층의 코드는 추상화된 기반 서비스 인터페이스를 통해서만 접근하도록 만들어서 특정 구현과 기술에 대한 종속성을 제거해야 함.
- e.g. MailSender
- 프레젠테이션 계층
- 주로 웹 기반의 UI 를 만들어내고 그 흐름을 관리
- 대부분의 엔터프라이즈 어플리케이션을 사용하는 클라이언트들은 HTTP 프로토콜을 선호. 따라서 이런 클라이언트와 연결돼서 동작하는 프레젠테이션 계층은 자바에서 http 프로토콜을 처리하는 가장 기본 엔진인 서블릿 기술을 바탕으로 함.
계층형 아키텍처 설계의 원칙
- 각 계층은 응집도가 높으면서 다른 계층과는 낮은 결합도를 유지해야 함
- 각 계층은 자신의 계층의 책임에만 충실
- e.g. 데이터 엑세스 계층에 비즈니스 로직을 담거나 웹 파라미터를 파싱하는 코드나 결과를 화면에 어떻게 뿌릴지 결정하는 코드가 들어가면 응집도가 낮아짐
- 계층 간 결합이 생기면 유연성이 떨어지기 때문에 각 계층의 내부 구현이 변화되면 다른 계층의 코드도 함께 수정해줘야 함.
- 또한 코드의 중복이 일어날 가능성이 높고 전체 코드를 이해하기 힘들어짐.
실수 예시
잘못된 예시
public ResultSet findUsersByName(String name) throws SQLException
올바른 예시
public List<User> findUsersByName(String name) throws DataAccessException
잘못된 예시
- 프레젠테이션 계층의 오브젝트를 그대로 서비스 계층으로 전달하는 것
- e.g. HttpServletRequest, HttpSession 과 같은 타입을 서비스 계층 인터페이스 메소드의 파라미터 타입으로 사용하면 안 됨
Q. 790p. 계층 사이의 호출은 인터페이스를 통해 이뤄져야 한다(?)
어플리케이션 정보 아키텍처
- 어플리케이션을 사이에 두고 흘러다니는 정보를 어떤 식으로 다룰지를 결정하는 일도 아키텍처를 결정할 때 매우 중요한 기준이 됨
- 데이터 중심 아키텍처
- DB/SQL 중심의 로직 구현 방식
- 거대한 서비스 계층 방식
- 오브젝트 중심 아키텍처
- 빈약한 도메인 오브젝트 방식
- 풍성한 도메인 오브젝트 방식
데이터 중심 아키텍처
- 애플리케이션에 흘러다니는 정보를 단순히 값이나 값을 담기 위한 목적의 오브젝트 형태로 취급하는 구조
- DB 에서 돌려주는 내용을 그대로 맵이나 단순 결과 저장용 오브젝트에 넣어서 전달.
DB/SQL 중심의 로직 구현 방식
- 하나의 업무 트랜잭션에 모든 계층의 코드가 종속되는 경향
- 주요 비즈니스 로직이 SQL과 DB에 존재
- SQL의 결과는 컬럼 이름을 키로 갖는 맵에 저장되거나 조회 페이지에 필요한 네 가지 정보를 담을 수 있는 단순한 오브젝트에 저장되어 전달됨.
- 장점
- 처음에는 개발하기 쉬움
- 단점
- 자바 코드를 단지 DB와 웹 화면을 연결해주는 단순한 인터페이스 도구로 전락시킴
- 변화에 매우 취약하다
- 각 계층의 코드가 긴밀하게 연결되어 있기 떄문
- 중복 제거가 쉽지 않음
거대한 서비스 계층 방식
- DB 에 많은 로직을 두는 개발 방법의 단점을 피하면서 어플리케이션 코드의 비중을 높이는 방법.
- 여전히 SQL 의 결과를 그대로 담고 있는 단순한 오브젝트 또는 맵을 이용해 데이터를 주고 받음
- 상대적으로 단순한 DAO 로직을 사용하고 비즈니스 로직의 대부분을 서비스 계층에 집중하는 방식 -> 거대한 서비스 계층 (fat service layer)
- 장점
- 어플리케이션 코드에 비즈니스 로직이 담겨 있기 때문에 자바 언어의 장점을 활용해 로직 구현 가능. 테스트 수월
- 단점
- SQL은 서비스 계층의 비즈니스 로직의 필요에 따라 만들어지기 쉬움
- 계층 간 결합도가 여전히 크다
- 비슷한 코드가 중복돼서 나타나기 쉬움
- SQL은 서비스 계층의 비즈니스 로직의 필요에 따라 만들어지기 쉬움
데이터 중심 아키텍처 문제점
- 계층 사이의 결합도가 높고 응집도가 떨어진다.
오브젝트 중심 아키텍처
- 도메인 모델을 반영하는 오브젝트 구조를 만들어두고 그것을 각 계층 사이에서 정보를 전송하는 데 사용
- 오브젝트를 만들어두고 오브젝트 구조 안에 정보를 담아서 각 계층 사이에 전달하게 만드는 것
데이터 vs 오브젝트
- 예시
- Category, Product 라는 두 개의 entity 가 있는 경우
- 조건에 맞는 모든 카테고리와 상품 정보를 가져와서 화면에 출력하는 기능 구현
데이터
//sql 결과를 맵에 담는 DAO 코드
while (rs.next()) {
Map<String, Object> resMap = new HashMap<>();
resMap.put("categoryId", rs.getString(1));
resMap.put("description", rs.getString(2));
//...
list.add(resMap);
}
- 서비스 계층에 전달되느 것은 List<Map<String, Object>> 타입
- 위 결과를 사용하는 서비스 계층의 코드에서는 DAO 메소드에서 가져온 필드 값의 종류, 이름을 알아야만 한다. 또한 DAO 에서 필드 개수나 순서, 이름을 바꾸면 서비스 계층의 코드도 함께 변경해야 함.
- DAO 가 만드는 SQL 결과에 모든 계층의 코드가 의존하게 됨
오브젝트
public class Category {
int categoryid;
String description;
//자바 레퍼런스를 통해 products 참조 가능
Set<Product> products;
}
public class Product {
int productid;
String name;
int price;
//자바 레퍼런스를 통해 category 참조 가능
Category category;
}
- 어플리케이션 어디에서도 사용될 수 있는 일관된 형식의 도메인 정보를 담고 있음
- dao는 자신이 DB에서 가져와서 도메인 모델 오브젝트에 담아주는 정보가 어떤 업무 트랜잭션에서 어떻게 사용될지 신경쓰지 않아도 됨
- 서비스 계층 또한 DAO 에서 어떤 sql을 사용했는지 몰라도 됨
도메인 오브젝트 사용
장점
- 코드를 이해하기 쉽고 로직 작성이 수월함
- e.g. category 내 product price 합 구하기
- e.g. 특정 product 가 속한 category 의 product 가 몇 개 인지 구하기
- 테스트 수월
- 코드를 이해하기 쉽고 로직 작성이 수월함
단점
- 최적화된 sql 을 매번 만들어 사용하는 경우에 비해 성능상 손해
- 도메인 오브젝트의 모든 필드 정보를 채워서 전달하는 것이 낭비일 수 있음
- 해결방법
- 지연된 로딩 기법
- ORM 기술 사용
빈약한 도메인 오브젝트 방식
- 도메인 오브젝트에 정보만 담겨 있고 정보를 활용하는 아무런 기능도 갖고 있지 않는 경우
- 다루는 정보의 구조가 다를 뿐이지 빈약한 도메인 오브젝트 방식은 데이터 중심 아키텍처의 거대 서비스 계층구조와 비슷하다
- 서비스 계층의 메소드에 대부분의 비즈니스 로직이 들어 있기 때문에 로직의 재사용성이 떨어지고 중복 문제가 발생하기 쉬움
풍성한 도메인 오브젝트 방식
- 빈약한 도메인 오브젝트의 단점을 극복하고 도메인 오브젝트의 객체지향적인 특징을 잘 사용할 수 있도록 개선한 것
- 비즈니스 로직 중 특정 도메인 오브젝트나 그 관련 오브젝트가 가진 정보와 깊은 관계가 있는 로직을 서비스 계층의 코드가 아니라 도메인 오브젝트에 넣어주고 서비스 계층의 비즈니스 로직에서 이를 재사용하게 만드는 것
public class Category {
int categoryid;
String description;
List<Product> products;
public int calcTotalOfProductPrice() {
int sum = 0;
for (Product product : products) {
sum += product.getPrice();
}
return sum;
}
}
public class InventoryService {
//DI
private CategoryService categoryService;
public void complexInventoryAnalysis() {
//...
int total = this.categoryService.calcTotalOfProductPrice(category);
}
}
- 각 비즈니스 로직을 담고 있는 서비스 오브젝트를 DI 해서 로직을 담은 메소드를 호출
public class InventoryService {
public void complexInventoryAnalysis() {
//...
int total = category.calcTotalOfProductPrice();
}
}
Category 오브젝트에게 필요한 계산 작업을 요청.
CategoryService 를 DI 받을 필요 없음.
간결하고 객체지향적인 코드
비슷한 코드가 여기저기 비즈니스 로직에 중복되어 나타나지 않음
풍성한 도메인 오브젝트 방식을 사용하더라도 서비스 계층은 여전히 필요하다.
- 도메인 오브젝트는 스프링이 관리하는 빈 오브젝트가 아님. DAO 나 서비스 오브젝트 같은 빈 기능 사용 불가능
- 수식 계산이나 조건에 따른 데이터의 변경 또는 자신이 가진 정보에 대한 분석 같은 도메인 오브젝트 자신에 국한된 로직은 도메인 오브젝트 안에 추가할 수 있지만 그 결과를 DB 에 저장하거나 메일로 발송하거나 DB를 검색해서 원하는 정보를 가져와 활용하는 작업은 도메인 오브젝트에서는 불가능. 서비스 계층의 코드가 필요.
도메인 계층 방식
- 도메인 오브젝트가 스스로 필요한 정보를 DAO를 통해 가져올 수 있고, 생성이나 변경이 일어났을 때 직접 DAO에게 변경사항을 반영해달라고 요청하게 만들고 싶다.
- 도메인 오브젝트가 기존 3계층과 같은 레벨로 격상되어 하나의 계층을 이루게 하는 것
- 서비스 계층의 도움 없이 비즈니스 로직의 대부분의 작업을 수행
- 도메인 오브젝트는 스프링 빈이 아니기 때문에 DI를 적용하기 위해서는 특별한 설정이 추가되어야 함
- Aspect J 활용 -> 오브젝트가 만들어지는 시점을 조인 포인트로 사용
도메인 계층 도입시 고려할 점
- 도메인 오브젝트가 도메인 계층을 벗어나서도 사용되게 할 것인지?
- 모든 계층에서 도메인 오브젝트 사용
- 막강한 기능을 가진 도메인 오브젝트를 프레젠테이션 계층이나 뷰 등에서 사용하게 해주면 이를 함부로 사용하는 위험 발생
- 개발 정책
- AspectJ 정책/표준 강제화 기능
- 막강한 기능을 가진 도메인 오브젝트를 프레젠테이션 계층이나 뷰 등에서 사용하게 해주면 이를 함부로 사용하는 위험 발생
- 도메인 오브젝트는 도메인 계층을 벗어나지 못하도록 함
- 도메인 계층 밖으로 전달될 때는 별도의 정보 전달용 오브젝트(Data Transfer Object) 에 도메인 오브젝트 내용을 복사해서 넘겨줌
- 모든 계층에서 도메인 오브젝트 사용
- 도메인 계층이 유용한 경우
- 매우 복잡하고 변경이 잦은 도메인을 가졌을 때
- 도메인이 가진 복잡함을 객체지향적인 설계의 모든 장점을 동원해서 유연하게 대응 가능
- 매우 복잡하고 변경이 잦은 도메인을 가졌을 때
스프링 어플리케이션을 위한 아키텍처 설계
계층형 아키텍처
- 3계층 아키텍처 응용
- 서비스 계층과 데이터 엑세스 계층을 통합
- 프레젠테이션 계층과 서비스 계층을 통합 -> 권장 X
- 트랜잭션 경계 설정 애매
- 스프링을 처음 학습한다면 3계층 구조에 먼저 익숙해진 후 차차 다양한 방식으로 계층구조의 통합과 분산 시도
정보 전송 아키텍처
- 우선 빈약한 도메인 오브젝트 방식으로 시작하는 것이 가장 쉽다.
- 가능하다면 도메인 오브젝트에 단순한 기능이라도 추가하도록 노력해보는 게 좋다.
- 객체지향적인 도메인 분석과 모델링에 자신이 있고 도메인 오브젝트 설계와 구현, 독립적인 테스트를 자유롭게 적용할 수 있다면 도메인 계층 방식 도입
상태 관리와 빈 스코프
- 아키텍처 설계시 ‘상태 관리’ 를 어떻게 할 것인지 신경써야 함.
- 스프링은 기본적으로 상태가 유지되지 않는 빈과 오브젝트 사용을 권장
- 클라이언트, 백엔드에 저장
- HTTP 세션 활용
- 싱글톤 이외의 다른 스코프를 갖는 빈 활용
스프링이 지원하는 기술이란?
- 해당 기술을 스프링의 DI 패턴을 따라 사용 가능
- 혹은 프레임워크나 라이브러리의 핵심 클래스를 빈으로 등록할 수 있게 지원
- 스프링의 서비스 추상화가 적용
- 비슷한 기능을 제공하는 기술에 대한 일관된 접근 방법을 정의해줌
- 필요에 따라 호환 가능한 기술로 손쉽게 교체해서 사용
- 스프링이 지지하는 프로그래밍 모델 적용
- e.g. 데이터 엑세스 기술에 대한 일관된 예외 적용
- 불필요하게 예외를 처리하는 코드를 피하도록 런타임 위주의 예외르 사용한다는 스프링의 개발철학이 적용된 예시
- e.g. 데이터 엑세스 기술에 대한 일관된 예외 적용
- 템플릿/콜백 지원
- e.g. JDBC
- 대부분의 템플릿 클래스는 빈으로 등록해서 필요한 빈에서 DI 받아 사용 가능
- 스프링에 새로운 기술을 연동하려면 스프링의 프로그래밍 모델과 개발 철학을 따르면서 위 네가지 방법을 이용하면 된다.
- 스프링을 사용하려면 스프링의 프로그래밍 모델과 개발 철학을 따르는 일관된 코드를 만드는 데 많은 관심을 기울여야 한다.
'SPRING' 카테고리의 다른 글
토비의 스프링 10. 스프링이란 무엇인가 (0) | 2022.09.18 |
---|---|
토비의 스프링 9. 스프링 핵심 기술의 응용 (2) (0) | 2022.09.18 |
토비의 스프링 8. 스프링 핵심 기술의 응용 (1) (0) | 2022.09.18 |
토비의 스프링 7. AOP (2) (0) | 2022.09.18 |
토비의 스프링 6. AOP (1) (0) | 2022.05.27 |
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
- Total
- Today
- Yesterday
링크
TAG
- 토비
- provider
- c++
- OOP
- 자바
- 코테
- 프록시패턴
- 토비의봄TV
- gracefulshutdown
- 프로그래머스
- SOLID
- 템플릿콜백
- 자바스터디
- 예외처리
- 데코레이터패턴
- ec2
- 백기선
- java
- 디자인패턴
- 토비의스프링
- 프록시
- 코딩테스트
- 김영한
- 객체지향
- AOP
- 메서드레퍼런스
- 스프링
- 카카오
- 서비스추상화
- BOJ
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |
글 보관함