지난주에는 JWT Token을 이용하여 로그인 기능을 구현하는 연습을 하면서
기본CRUD에 유저정보를 넣는 작업을 하기위해 알아보면서
@AuthenticationPrincipal 어노테이션에 대해
알게되었습니다, 하지만 더 좋은 방법을 동료에게 추천을 받았다.
@AuthenticationPrincipal을 함으로서 User엔티티를 한번더 불러와
select쿼리가 낭비되는 것보다는 DTO역할 분리를 하고자하는
로직에 따라 분리하여 충분히 update쿼리 하나만으로도 어노테이션없이
쿼리낭비를 하지않으면서도 효울적이게 코드를 작성할 수 있다. .
@AuthenticationPrincipal
기존에는 로그인한 사용자의 정보를 파라메터로 받고 싶을때
@AuthenticationPrincipal 애노테이션을 사용하면
UserDetailsService에서 Return한 객체 를 파라메터로 직접 받아 사용할 수 있었다.
그런데 @AuthenticationPrincipal 애노테이션을 사용하면 편하고 좋겠지만
실무에서 사용하게된다면 @AuthenticationPrincipal User user 테이블을 조회하게되면서
굳이 한번 더 select쿼리가 나가게되면서 쿼리 낭비를 해야하는 로직을 사용하게 되는 것이다.
그렇다면 실무에서는 이럴 경우 @AuthenticationPrincipal 을
사용하지 않으면서 로직을 짤 수 있을까??
게시글 수정하기 기능을 예시로보면서 파악해보자.
글수정 기능에는 내가 쓴글이 아닌 글에는 수정이 불가하도록 처리해줘야한다.
그러기위해서는 사용자가 누구인지 알 수있는 로직이 필요할 것이다.
// 컨트롤러에서 수정을 위한 @RequestBody dto를 service에서 repository,로
queryDsl 로직을 수행하는 repositoryImpl에서update 쿼리를 탄다.
/** BoardController 글 수정 **/
@PutMapping("/editBoard")
fun editBoard(
@RequestBody dto: BoardModifyRequestDTO
): ResponseEntity<MyResponse<Boolean>> {
val result = boardService.editBoard(dto)
return getResponse(result)
}
// 이때 BoardModifyRequestDTO에
// 게시글 엔티티에서 생성자부분에
// user 정보를 같이 추가해준다.
/** 게시글 수정 DTO **/
class BoardModifyRequestDTO (
var board_id: Long,
var userId: Long,
var title: String,
var content: String,
) {
}
/**Board 엔티티**/
class BoardModifyRequestDTO (
var board_id: Long,
var userId: Long, // board와 연관관계 맺은 user엔티티의 pk를 가져온다.
var title: String,
var content: String,
) {
}
그래서 굳이 @AuthenticationPrincipal을 함으로서 User엔티티를 한번더 불러와 select쿼리가 낭비되는 것보다는 DTO역할 분리를 하고자하는 로직에 따라 분리하여 충분히 update쿼리 하나만으로도 어노테이션없이 쿼리낭비를 하지않으면서도 효울적이게 코드를 작성할 수 있다.
기능과 목적에 따라 DTO를 분리하는게 중요하다.
회사마다 MVC패턴이 조금씩 다른데, 회사에서 쓰는Mvc 패턴은 대충 이렇다.
(쓰는 부분이 있고 몇가지 생력하는 회사들도 있음)
🍃 Spring MVC Pattern
controller
SampleContoller(url 매핑, 서비스의 메서드 불러오기, 서비스에 뭘넘길래?)
service
SampleService( Controller에서 받은 데이터를 ServiceImpl의 메서드를 동해 넘겨라)
SampleServiceImpl(Service에서 받은 데이터를 Repository의 해당 메서드에 넘기는
Repository
repository (ServiceImpl을 통해 받은 데이터를 받아서 리턴타입과 일치하게 작성해준다. )
repositoryImpl( repository의 클래스를 override 해준다. 그리고 queryDsl 비지니스 로직을 작성해준다.)
domain (역할 분리를 위한 DTO, Entity)
.entity 1). db layer와 데이터를 주고받을 때 사용한다.
2) Column을 필드로 가지는 객체이다.
3) DB 테이블과 1:1 로 대응되는 객체)
dto 1.) view layer를 위한 클래스이기 때문에 RequertDTO, ResponseDTO로 분리한다)
2) Entity클래스와 Controller에서 쓸 DTO는 분리해야한다
3) 계층관 교환을 위한 순수한 데이터 객체
DTO 역할과 관심사의 분리 ..?
관심사의 분리 : Entity와 DTO를 분리해야 하는 가장 근본적인 이유는
관심사가 서로 다르기 때문이다.
서로 다른 관심사들을 분리하여 변경 가능성을 최소화하고,
DTO의 핵심 관심사는 이름 그대로 데이터의 전달이다.
DTO는 데이터를 담고,그러므로 어떠한 기능 및 동작도 없어야 한다.
그러므로 Entity 또는 도메인 객체는 그에 따른 비지니스 로직이 추가될 수 있다.
사용되는 객체가 아니다.Entity와 DTO는 엄연히 서로 다른 관심사를 가지고 있고,
Entity의 값이 변하면 Repository 클래스의 Entity Manager의 flush가 호출될 때View와 통신하면서
필연적으로 데이터의 변경이 많은 DTO클래스를 분리해주어야 한다.
- common/domain/User : DTO이기 때문에 Request, Response 처리할때 사용을 해주면 되고
- backend/domain/User : 실제 DB와 상호작용을 할때는 Entity객체를 사용을 하면 된다.
- 해결방안
- 때문에 이런 경우에는 분리한 DTO에 Presentation로직 정도를 추가해서 사용하고,
- 이 경우에 Entity와 DTO가 분리되어 있지 않다면
- 도메인 설계가 아무리 잘 되있다 해도 Getter만을 이용해서
- DB에 값이 반영되고, 이는 다른 로직들에도 영향 미친다.
- 그렇기 때문에 분리하는 것이 합리적이다.
- Entity 또는 도메인 객체는 다른 계층이나 컴포넌트들 사이에서 전달을 위해
- 반면에 Entity는 핵심 비지니스 로직을 담는 비지니스 도메인의 영역의 일부이다.
- 다른 계층 또는 다른 컴포넌트들로 데이터를 넘겨주기 위한 자료구조이다.
- 유연하며 확장가능한 클린 아키텍처를 구축하도록 도와준다.
사실 이런글보다는 본인이 실제로 코드를 짜보면서
뭐가 불편한지 편한지 몸소 체험하시는 편이 제일 빠를겁니다!
'Back-end Skill > Springboot' 카테고리의 다른 글
스프링을 사용하여 로그를 남기고 로그 파일 추출하는 법 (0) | 2022.12.29 |
---|---|
[Springboot] 데이터 인자를 넘겨받는 각기 어노테이션 : 목적으로 다르게 효율적으로 쓰일 수 있는 방법들 (0) | 2022.10.24 |
Hibernate JPA 사용할 때 주의해야할 점. 원리를 모르면 그냥 쓰지마세요 1편 (9) | 2022.10.05 |
면접 스터디 2주차 자바와 스프링 프레임워크 (0) | 2022.02.15 |
스프링 MVC 정리본 1 (0) | 2022.02.12 |