1. spring project도 아닌데 왜 mvc 패턴을 고려?
자동차 경주 게임을 구현할 때까지만 해도 크게 중요성을 느끼지 못하고 있었다. 내가 만든 분류 안에서 코드가 다 동작했으니...하지만 TDD를 수행할 때 문제점을 깨달았다. 내가 분류한 조건들이 단일체계원칙을 실질적으로 지키고 있지 않았기 때문에 TDD를 수행할 수가 없었다.
출력과 기능이 섞여서 method는 대부분 void로 반환 값이 존재하지 않았고 TDD로 검증도 불가...
향후 리팩토링을 할 경우에도 TDD가 안되니 수정 코드가 올바르게 작동한다는 보장이 없다...
해당 문제점을 인지하고 어떤 방식으로 패키지를 분류해야할지 고민하고 찾아보던 중 mvc 패턴을 사용한다는 힌트를 얻었다.
꼭 Spring이 아니더라도 해당 구조로 분류 및 설계를 진행하면 단일체계원칙도 지키면서 앞으로도 일관된 방식으로 코드를 작성을 할 수 있을 것이라는 느낌을 받았다.
그래서 mvc 패턴을 학습, 패키지 구성을 하여 해당 방식을 적용하고 향후 mvc 패턴이란 큰 틀 내에서 디자인 패턴, java code 컨벤션 을 적용할 예정이다.
2. MVC 패턴에 따른 패키지 분류
2.1. 작은 단위 패키지 구성
- 제일 작은 단위로 보았을 때 아래와 같이 구성
common
util
└── Validator.java
└── Parser.java
domain
└── Domain.java (class || enum)
└── DomainDto.java (class)
view
└── Input.java
└── Output.java
controller
└── Controller.java
service
└── Service.java
repository
└── Repository.java
2.2. 규모가 커졌을 경우 - 계층형
order
└── common
└── util
└── Validator.java
└── Parser.java
└── domain
└── Domain.java (class || enum)
└── DomainDto.java (class)
└── view
└── Input.java
└── Output.java
└── controller
└── Controller.java
└── service
└── Service.java
└── repository
└── Repository.java
2.3. 규모가 커졌을 경우 - 도메인형
common
util
└── order
└── Validator.java
└── Parser.java
domain
└── order
└── Domain.java (class || enum)
└── DomainDto.java (class)
view
└── order
└── Input.java
└── Output.java
controller
└── order
└── Controller.java
service
└── order
└── Service.java
repository
└── order
└── Repository.java
2.4. 스프링 프로젝트 패키지 구조
- 일반적 구조
- Controller(또는 API)
- Service(또는 application)
- Repository(또는 DAO)
- Model(또는 Domain , DTO) : 동일하게 써도 되고 분리해도 된다.
- Configuration(구성)
- Util(유틸리티)
- Exception(예외)
- Security(보안)
- global - 전역적으로 사용되는 것으로 auth, common, config, error, infra, util 등이 존재
3. MVC 패턴
- 참조 블로그
- MVC > spring MVC: MVC가 spring MVC보다 포괄적인 개념
- Model : Service, Repository(DAO), DTO, Domain
- View : Front-end
- Controller: Controller
- 의존하지 않는다의 의미 : 해당 class에 의존하지 않는 class가 존재조차 하면 안된다. - 매개변수로도 존재하면 안된다.
3.1. Model
- 참고로, spring mvc의 구체적인 model객체와 다른 의미이다.
- (도메인)애플리케이션의 정보, 데이터를 나타낸다.
- 데이터베이스, 처음의 정의하는 상수, 초기화값, 변수 등을 의미
- 추가로 이러한 데이터를 파싱하거나 데이터베이스와의 통신 또한 책임지는 컴포넌트(비지니스 로직)
- 모델의 규칙
1. 사용자가 편집하길 원하는 모든 데이터를 가지고 있어야 한다.
2. 뷰나 컨트롤러에 대해서 의존해서는 안된다.
3. 변경이 일어나면, 변경 통지에 대한 처리방법을 구현해야만 한다.
3.2. View
- 사용자가 보는 화면, 즉 input 텍스트, 체크박스 항목 등과 같은 사용자 인터페이스 요소를 의미한다.
- 데이터를 기반으로 사용자들이 볼 수 있는 화면이다.
- 뷰의 규칙
1. 모델이 가지고 있는 정보를 따로 저장해서는 안된다.
2. 모델에는 의존해도 되지만, 컨트롤러의 의존해서는 안된다.
3. 사용자마다 다르게 보여줘야하는 데이터만 받아야한다.
4. view가 Model로 부터 data를 받을 때는 반드시 controller를 통해서 받아야한다.
5. 변경이 일어나면 변경 통지에 대한 처리방법을 구현해야만 한다.
3.3. Controller
- Model과 View에 의존가능하다.
- 즉, 사용자가 데이터를 클릭하고, 수정하는 것에 대한 "이벤트"들을 처리하는 부분을 의미한다.
- 컨트롤러의 규칙
1. Model과 View에 의존가능하다.
2. 모델이나 뷰의 변경을 모니터링 해야 한다.
4. Spring MVC 구조
4.1. Web Layer(API, Controller)
- 컨트롤러(@Controller)가 대표적이고, 이외에도 필터(@filter), 인터셉터, 컨트롤러 어드바이스 등이 포함된다.
- 외부 요청과 응답에 대한 전반적인 영역을 의미한다. (API)
4.2. Service
- 비즈니스 로직
4.3. Repository (DAO)
- DAO(Data Access Object)는 데이터 베이스에 접근하기 위한 객체이다.
- DataBase에 접근하기 위한 로직 & 비지니스 로직을 분리하기 위해 사용
Repository와 DAO 차이점
Repository는 주로 도메인 객체와 연결된 데이터 액세스 계층을 만들기 위해 사용되는 패턴이며, 높은 수준의 추상화를 제공합니다. 반면에 DAO는 데이터베이스와의 접근을 캡슐화하고 데이터 액세스 작업을 처리하는 데 사용됩니다. Repository는 일반적으로 ORM과 통합되는 데 반해, DAO는 직접적인 SQL 쿼리나 JDBC와 같은 데이터베이스 API와 함께 사용됩니다.
4.2. Domain
- 도메인 객체는 일반적으로 데이터베이스의 엔터티와 일치하며, 비즈니스 로직을 처리하기 위한 메서드를 가질 수 있다.
- 간단한 로직인 경우에 domain에서 로직까지 처리하고 service계층을 생략할 수 있지만 서비스 로직이 복잡하면 분리하는 것이 좋다.
- 도메인 객체는 애플리케이션의 핵심 도메인 개념을 나타내는 객체(애플리케이션의 핵심 데이터 모델을 대표)로써
- 1) 데이터베이스와 상호작용 하고 2) 비즈니스 로직을 포함하며, 실제 비즈니스 요구 사항을 반영
- 예를 들어, 은행 애플리케이션에서 "계좌" 또는 "고객"은 도메인 객체로 나타낼 수 있다.
- 개발 대상, 즉 도메인을 모든 사람이 동일한 관점에서 이해할 수 있고 공유할 수 있도록 단순화 한 것
- JPA를 사용한다면, @Entity가 사용되는 영역 또한 도메인 영역
도메인 객체(Domain Object)는 데이터베이스에 값을 저장하는 객체일 뿐만 아니라 비즈니스 로직을 포함하고 처리하는 객체입니다. 이는 도메인 주도 디자인(Domain-Driven Design, DDD)이라는 소프트웨어 개발 패러다임의 핵심 원리 중 하나입니다. 이 개념은 데이터베이스 레코드를 단순한 데이터 저장소로 보는 것을 벗어나, 애플리케이션의 핵심 비즈니스 도메인에 해당하는 객체를 모델링하고 그 안에 비즈니스 로직을 포함시키는 개발 방법론입니다.
도메인 객체가 비즈니스 로직을 포함하는 이유는 다음과 같습니다:
비즈니스 논리의 집중: 도메인 객체는 비즈니스 업무와 관련된 데이터와 동작을 캡슐화합니다. 이로 인해 비즈니스 논리가 애플리케이션 전체에 분산되지 않고 도메인 객체 내에 집중됩니다. 이는 유지보수성과 코드 이해성을 향상시킵니다.
응집성(Cohesion) 강화: 비즈니스 논리를 관련된 데이터와 함께 유지하는 것은 객체의 응집성을 높이는데 도움이 됩니다. 응집성이 높은 객체는 특정 작업 또는 업무와 관련된 모든 것을 함께 묶어 더 이해하기 쉽게 만듭니다.
재사용성: 도메인 객체 내에 비즈니스 로직을 포함하면 이 로직을 다른 부분에서 재사용하기 쉬워집니다. 예를 들어, 동일한 비즈니스 규칙을 여러 컴포넌트에서 사용하거나, 다른 애플리케이션에서 도메인 객체를 활용할 수 있습니다.
도메인 주도 디자인: DDD는 도메인 주도 디자인의 원리에 따라 도메인 모델을 설계하는데 중점을 두며, 도메인 객체를 사용하여 애플리케이션의 핵심 도메인을 표현합니다.
요약하면, 도메인 객체가 비즈니스 로직을 포함하는 것은 데이터베이스 저장소 이상의 역할을 수행하고, 애플리케이션의 핵심 도메인을 모델링하고 처리하기 위한 객체로 도메인 주도 디자인 패러다임의 중요한 개념입니다.
4.3. DTO
- 계층 간 데이터의 전달과 관련된 역할을 수행. 주로 데이터 전송의 효율성을 높이기 위해 사용하는 객체
- 뷰에서 필요한 데이터를 포함하며, 뷰(View)와 컨트롤러(Controller) 간의 데이터 전달을 간단하게 관리하고 형식화하는 데 사용
- 일반적으로 뷰에서 필요한 데이터만을 포함하고, 데이터베이스 또는 도메인 객체의 일부 정보를 필터링하여 제공
- DTO는 로직을 가지지 않는 순수한 데이터 객체(getter & setter 만 가진 클래스)
4.4. VO
- VO(Value Object)는 값 오브젝트로써 값을 위해 사용.
- read-Only 특징(사용하는 도중에 변경 불가능하고, 오직 읽기만 가능)을 가지고 있다.
- DTO와 유사하지만 VO는 getter만 가진 클래스
5. 추가
5.1. util 패키지
- 유틸리티 함수 및 메서드
- 여러 레이어에서 공통으로 사용되는 유틸리티 함수나 도우미 메서드를 그룹화하는 용도로 사용
- ex) 날짜 형식 변환, 문자열 조작, 파일 조작과 같은 일반적인 작업을 수행하는 메서드를 "util" 패키지에 정의
- 설정 관리
- 시스템 설정이나 구성 관리에 관련된 클래스와 메서드를 "util" 패키지에 저장
- 설정은 여러 레이어에서 공통으로 사용될 수 있으며, 중복을 피하기 위해 "util" 패키지에서 관리
- 다양한 서비스에 대한 클라이언트 라이브러리
- 여러 서비스 또는 API와 상호 작용하기 위한 클라이언트 라이브러리를 "util" 패키지에 두고 여러 레이어에서 공유
- 커스텀 예외
- 애플리케이션에서 발생하는 예외 클래스를 정의하고 "util" 패키지에 위치
5.2. 검증 파일
- mvc, spring mvc 패턴에서 목적에 맞게 각각 사용 된다.
- mvc
- model : 데이터 검증. 비즈니즈 로직 검증
- controller : 사용자 정보 검증
- spring mvc
- controller : 사용자 정보 검증
- service: 비즈니즈 로직 검증
- repository: 데이터 무결성 검증 - 가능은 하지만 db 제약 조건으로 해결하는 것을 권장