개요
최근 김영한님의 책을 보면서 JPA를 공부하고 있는데, 이를 적용해보려면 아무래도 스프링 프로젝트 코드도 작성해야 했다. 그렇기에 일단 스프링 구조를 정리해보면서 어떤식으로 적용해볼 수 있는지 알아보고자 했다.
0. 계층 분리 이유?
어떤 기술을 사용하던 항상 왜??? 사용하는지에 대해 아는 것은 정말 중요하다고 생각한다.
따로 패키지 구조를 나눈 이유가 뭘까?
-> 각 계층에 대해 명확한 책임을 분리했을 때, 코드의 가독성이 높아진다.
-> 또한 계층 별로 코드를 변경할 때 다른 계층에 미치는 영향을 최소화하여 확장에 용이하다.
-> 계층에 따른 테스트를 진행할 수 있어, 단위 테스트 및 통합 테스트 작성이 쉬워진다.
1. Spring Boot 프로젝트 구조
일단, 그림을 통해 정리해봤다.
스프링 부트에서는 프레젠테이션, 비즈니스, 퍼시스턴스 계층으로 구분할 수 있다.
1) 프레젠테이션 계층(Presentation Layer)
-> 사용자의 요청을 처리하고, 요청에 대한 응답 데이터를 반환한다.
-> 클라이언트와 서버 간의 인터페이스 역할을 담당하며 HTTP 요청 및 응답을 처리한다.
-> @RestController 또는 @Controller를 사용해 API 엔드포인트를 정의한다.
2) 비즈니스 계층(Business Layer)
-> 핵심 비즈니스 로직을 처리한다.
-> 요청을 검증하고, 데이터 로직을 수행한다.
-> 필요한 경우에 퍼시스턴스 계층과 통신한다.
-> @Service 어노테이션을 사용해 구현한다.
3) 퍼시스턴스 계층(Persistence Layer)
-> 데이터베이스와 상호작용해 데이터를 저장하거나 조회한다.
-> ORM(JPA, Hibernate), JPQL, SQL을 통해 데이터 조작을 수행한다.
-> @Repository 어노테이션을 사용해 구현한다.
-> DAO또는 Repository를 사용한다고 한다.
Entity와 DTO에 대한 내용도 정리해야 하는데 이것은 나중에 다른 글을 통해 따로 정리해보겠다.
이렇게 계층이 나눠져 있고, 각 계층별로 코드를 살펴보며 조금더 자세하게 정리해보자.
2. 계층별 코드
2-1. 프레젠테이션
UserController.java
@RestController
@RequestMapping("/users")
@RequiredArgsConstructor
public class UserController {
private final UserService userService;
@GetMapping("/{id}")
public ResponseEntity<UserDto> getUserById(@PathVariable Long id) {
UserDto user = userService.getUserById(id);
return ResponseEntity.ok(user);
}
}
프레젠테이션에 해당하는 Controller이다.
클라이언트에 요청을 받아 Service 계층에게 요청을 전달하고, 요청된 결과를 클라이언트에 반환해준다.
: @RestController
-> RESTful API 엔드포인트를 정의하는 클래스
: @RequestMapping("/users")
-> 이 컨트롤러의 기본 URL 경로를 /users 로 설정한다.
: @RequiredArgsConstructor
-> Lombok을 활용해 생성자를 자동 생성하고, final로 선언된 UserService를 주입받는다.
: @GetMapping
-> HTTP GET 요청을 처리하는 메서드를 정의한다.
2-2. 비즈니스
UserDto.java (DTO)
@Getter
@AllArgsConstructor
public class UserDto {
private Long id;
private String name;
private String email;
}
데이터 전송을 위한 객체로 사용되며, 엔티티와 분리되 비즈니스 및 프레젠테이션 게층 간의 데이터 전달에 사용된다.
UserService.java
@Service
@RequiredArgsConstructor
public class UserService {
private final UserRepository userRepository;
public UserDto getUserById(Long id){
User user = userRepository.findById(id)
.orElseThrow(() -> new RuntimeException("확인되지 않은 id " + id);
return new UserDto(user.getId(), user.getName(), user.getEmail());
}
}
비즈니스 로직을 처리하고, Repository를 호출해 데이터를 조회하거나 저장한다.
DTO를 사용해서 데이터를 변환하고 이를 Controller에 반환한다.
: @Service
-> 이 클래스가 비즈니스 로직을 처리하는 역할을 담당하는 것을 나타낸다.
2-3. 퍼시스턴스
User.java (Entity)
@Entity
@Table(name = "users")
@Getter
@NoArgsConstructor
@AllArgsConstructor
public claass User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String email;
}
데이터베이스 테이블의 구조를 표현한다.
: @Entity
-> 데이터베이스 테이블과 매핑되는 클래스임을 나타낸다.
: @Table
-> 테이블 이름을 지정한다.
: @Id & @GeneratedValue
-> 기본 키와 자동 생성 전략을 정의한다.
UserRepository.java
@Repository
@RequiredArgsConstructor
public class UserRepository {
@PersistenceContext
private final Entitymanager entityManager;
public Optional<User> findById(Long id) {
return Optional.ofNullable(entityManager.find(User.class, id));
}
}
데이터베이스와 직접적으로 상호작용하며 CRUD 작업을 수행한다.
: @Repository
-> 데이터 접근 계층의 역할을 나타낸다.
: EntityManager
-> JPA를 통해 데이터베이스와 상호작용한다.
정리
스프링 부트의 계층은 유지보수 및 가독성 그리고 테스트 코드의 작성을 용이하기 위해 나뉘어져 있다.
계층으로는 프레젠테이션, 비즈니스, 퍼시스턴스 계층이 있고 각 계층을 위해
Controller, DTO, Service, Repository & DAO, Entity로 패키지를 나눠 프로젝트를 개발할 수 있다.
이제 실제로 해보면서 어느 정도 익숙하게 해봐야겠다.
'Back-end > Server' 카테고리의 다른 글
[Server] 웹서버 VS 웹 어플리케이션 서버(WAS) (0) | 2024.06.02 |
---|---|
[Spring] 스프링 빈이란? (0) | 2024.05.28 |
[Spring] Spring 프레임워크? (0) | 2024.05.27 |