Layered Architecture
📌 애플리케이션을 세 가지 주요 계층으로 나누어 구조화하는 방법으로 각 계층은 특정한 책임을 갖고 있으며, 계층 간에는 명확한 역할 분담이 이루어져 코드의 재사용성, 유지보수성, 확장성을 높이는 데 도움을 준다.
주요 특징
- 계층 분리:
- 시스템을 기능별로 분리하여 모듈화.
- 책임 분리:
- 각 계층은 고유한 책임과 역할을 가짐.
- 상호 의존성:
- 상위 계층은 하위 계층에만 의존하며, 계층 간의 의존성을 제한.
- 유지보수 용이:
- 특정 계층의 변경이 다른 계층에 최소한의 영향을 미침.
Layerd Architecture 개요
- 기존의 MVC 패턴에서 Controller는 역할이 무수히 많다.
- 요청에 대한 처리
- 예외처리
- View Template 응답 or Data 응답
- 비지니스 로직 처리
- DB 상호작용
- 문제점
- Controller에서 요청에 대한 모든 처리를 수행한다. 즉, 책임이 너무 많다.
- 기능 추가, 수정, 삭제 등의 유지보수가 힘들어진다.
- 코드의 재사용성이 떨어진다. 메서드로 분리하여도 메서드를 호출하는 중복 코드가 발생한다.
Layered Architecture 구조
- Presentation Layer
- 사용자의 요청을 받고 응답하는 역할을 수행한다.
- 화면을 응답하거나 데이터를 응답하는 API를 정의한다.
- Business Layer(Service Layer)
- 비지니스 로직을 수행한다.
- 요청을 해석하여 Repository Layer에 전달한다.
- 일반적으로 하나의 비지니스 로직은 하나의 트랜잭션으로 동작한다.
- Data Access Layer(Repository Layer)
- 데이터베이스와 연동되어 실제 데이터를 관리한다.
- 용어 설명
- DTO(Data Transfer Object)
- 계층간 데이터 전달을 위해 사용되는 객체이다.
- Model
- Entity
- 추후 숙련주차에 배울 JPA와 관련이 있다.
- JPA에서는 Entity라는 형태로 데이터를 반환한다.
- Entity
- DAO(Data Access Object)
- DTO(Data Transfer Object)
계층 간 의존성
- 상위 계층은 하위 계층에만 의존합니다.
- 계층 간의 의존성을 단방향으로 제한하여 결합도를 낮춥니다.
계층 간 의존성 예시
Presentation → Application → Domain → Data Access
장점
- 유지보수성:
- 계층별 역할이 분리되어, 특정 계층의 변경이 다른 계층에 미치는 영향을 최소화.
- 재사용성:
- 서비스나 데이터 계층을 다른 애플리케이션에서도 재사용 가능.
- 테스트 용이성:
- 계층 단위로 테스트가 가능하여, 단위 테스트와 통합 테스트를 쉽게 수행.
- 확장성:
- 특정 계층에 새로운 기능을 추가하거나 변경하기 쉬움.
단점
- 복잡성 증가:
- 계층 간의 통신 코드로 인해 초기 개발이 복잡해질 수 있음.
- 성능 문제:
- 계층 간 호출이 많아지면 성능 저하 가능.
- 단순 CRUD에 과한 구조:
- 작은 애플리케이션에서는 계층형 아키텍처가 불필요한 복잡성을 초래.
계층형 아키텍처를 사용할 때 적합한 경우
- 복잡한 비즈니스 로직:
- 다양한 데이터 소스와 복잡한 비즈니스 규칙이 있는 애플리케이션.
- 협업 프로젝트:
- 역할 분리가 명확해 팀 간 작업이 효율적.
- 대규모 애플리케이션:
- 변경 사항을 쉽게 관리하고, 시스템 확장이 필요한 경우.
Layered Architecture 적용
1. Controller
- 클라이언트의 요청을 받는 역할을 수행한다.
- 요청에 대한 처리를 Service Layer에 전달한다.
- Service에서 처리 완료된 결과를 클라이언트에 응답한다.
- 사용하는 Annotation : @Controller, @RestController
2. Service
- 사용자의 요청 사항을 처리한다.
- DB와 상호작용이 필요한 경우, Repository Layer에게 요청한다.
- 사용하는 Annotation: @Service
3. Repository
- DB와 상호작용을 수행한다.
- Connection 연결, 해제
- CRUD 작업 처리
- 사용하는 Annotation: @Repository
4. DTO(Data Transfer Object)
- 계층간 데이터 전달을 위해 사용된다.
- 요청 데이터를 처리하는 객체는 일반적으로 RequestDto로 명명한다.
- 응답 데이터를 처리하는 객체는 일반적으로 ResponseDto로 명명한다.
DTO (Data Transfer Object)
📌 애플리케이션에서 계층 간 데이터 전송을 목적으로 사용하는 단순한 객체입니다.
- 주로 데이터를 전송하기 위한 속성과 Getter/Setter 메서드만 포함하며, 비즈니스 로직을 포함하지 않습니다.
DTO의 주요 역할
- 데이터 캡슐화:
- 데이터 구조를 명확히 정의하여 계층 간 데이터 교환을 표준화.
- 안전한 데이터 전달:
- 데이터 모델(Entity)과 직접 연관되지 않아, 민감한 데이터 보호 가능.
- 데이터 변환 및 제한:
- 클라이언트가 필요로 하는 데이터만 전달하도록 제한.
- API 설계에 유용:
- RESTful API에서 응답(Response) 또는 요청(Request) 데이터를 정의.
DTO의 구조
DTO는 일반적으로 다음과 같은 형태로 작성됩니다:
- 속성 필드
- Getter/Setter 메서드
- (선택적) 생성자, toString(), equals(), hashCode()
public class UserDTO {
private Long id;
private String name;
private String email;
// 기본 생성자
public UserDTO() {}
// 생성자
public UserDTO(Long id, String name, String email) {
this.id = id;
this.name = name;
this.email = email;
}
// Getter와 Setter
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
}
DTO의 사용 예
a. 요청(Request) DTO
클라이언트가 서버에 데이터를 보낼 때 사용.
Request DTO 예제
public class CreateUserRequest {
private String name;
private String email;
// Getter와 Setter
}
Controller
@RestController
@RequestMapping("/users")
public class UserController {
@PostMapping
public ResponseEntity<String> createUser(@RequestBody CreateUserRequest request) {
// 요청 데이터 처리
return ResponseEntity.ok("User created: " + request.getName());
}
}
요청 데이터
{
"name": "John Doe",
"email": "john.doe@example.com"
}
b. 응답(Response) DTO
서버가 클라이언트에 데이터를 반환할 때 사용.
Response DTO 예제
public class UserResponse {
private Long id;
private String name;
private String email;
public UserResponse(Long id, String name, String email) {
this.id = id;
this.name = name;
this.email = email;
}
// Getter만 제공
}
Controller
@RestController
@RequestMapping("/users")
public class UserController {
@GetMapping("/{id}")
public ResponseEntity<UserResponse> getUser(@PathVariable Long id) {
// 서비스에서 가져온 데이터
UserResponse response = new UserResponse(id, "John Doe", "john.doe@example.com");
return ResponseEntity.ok(response);
}
}
응답 데이터
{
"id": 1,
"name": "John Doe",
"email": "john.doe@example.com"
}
Entity와 DTO의 차이
Entity | DTO | |
역할 | 데이터베이스 테이블과 직접 매핑. | 데이터 전송용 객체. |
책임 | 비즈니스 로직 및 데이터베이스 작업 포함. | 단순히 데이터 전송 및 표현. |
위치 | Service, Repository 계층에서 사용. | Controller와 Service 계층 간 전송. |
직렬화 | 필요에 따라 가능. | 주로 직렬화되어 전송(JSON, XML 등). |
민감한 데이터 | 포함 가능. | 민감한 데이터는 제외. |
DTO 변환 (Entity ↔ DTO)
DTO는 Entity와 직접적으로 사용되지 않으며, 변환 과정을 거칩니다.
Service 계층에서 변환 예
@Service
public class UserService {
public UserResponse getUserResponse(UserEntity entity) {
return new UserResponse(
entity.getId(),
entity.getName(),
entity.getEmail()
);
}
}
Spring Boot에서 DTO 자동 변환
Spring Boot에서 DTO 변환을 쉽게 하기 위해 ModelMapper 또는 MapStruct와 같은 라이브러리를 사용할 수 있습니다.
a. ModelMapper 예제
의존성 추가 (Maven)
<dependency>
<groupId>org.modelmapper</groupId>
<artifactId>modelmapper</artifactId>
<version>3.1.0</version>
</dependency>
변환 코드
import org.modelmapper.ModelMapper;
@Service
public class UserService {
private final ModelMapper modelMapper = new ModelMapper();
public UserDTO convertToDTO(UserEntity entity) {
return modelMapper.map(entity, UserDTO.class);
}
}
DTO의 장점
- 데이터 보호:
- 데이터베이스와 직접 연결된 Entity를 노출하지 않아 민감한 데이터 보호.
- 역할 분리:
- Entity와 DTO를 분리하여 비즈니스 로직과 데이터 전송 책임을 명확히 구분.
- API 표준화:
- 클라이언트 요청과 응답 형식을 명확히 정의 가능.
- 유지보수성 향상:
- 데이터 전송 형식 변경이 Entity와 독립적으로 이루어짐.
결론
- DTO(Data Transfer Object)는 계층 간 데이터를 주고받을 때 사용되는 단순한 객체입니다.
- 데이터 캡슐화, 보안성 강화, API 설계 표준화 등의 장점으로, 특히 Spring MVC와 RESTful API 설계에서 널리 사용됩니다.
- DTO를 활용하여 애플리케이션의 구조를 더 유연하고 유지보수 가능하게 설계할 수 있습니다.
'Back-End (Web) > Spring' 카테고리의 다른 글
[Spring] Server에서 Client로 Data를 전달하는 방법 (0) | 2024.12.19 |
---|---|
[Spring] HTTP Message Body & TEXT (0) | 2024.12.18 |
[Spring] @RequestParam & @ModelAttribute (0) | 2024.12.17 |
[Spring] Request Mapping (1) | 2024.12.16 |
[Spring] Spring Annotation (1) | 2024.12.15 |