스프링은 데이터를 어떻게 주고 받을까? (24.05.16)

YJ·2024년 5월 16일
post-thumbnail

블로그 작성법
목표 > 공부한 내용 > 얻었고, 앞으로 이걸 해봐야지 적기

✋ 수업 목표

  • Domain Object <-> DTO 변환 방법 최소 4가지 중 기본 방법 1가지에 대해 배워보자.
  • MSA에 대해 배워보자.

🤔 공부한 내용

DTO (Data Transfer Object)

단어들의 뜻을 통해 유추해보면,
데이터 전송 객체 라는 의미이다.
왜 굳이 DTO를 사용할까 라는 생각이 든다.

그냥 Domain Object를 쓰면 안될까?

Domain Object의 단점과 DTO의 등장 이유

  1. 클라이언트에게 굳이 전달하지 않아도 되는 데이터를 포함하고 있다.
    ex. 시스템 활용 데이터 (생성/수정일시..)

  2. 필요한 데이터가 없을 수 있다?
    ex. 2000년 5월 15일 (만 24세)

  3. 계층 간에 전송(이동)시 데이터 변환에 위험이 있다.
    예를 들어, Domain Object가 Setter를 가지고 있다면...
    Setter의 특징은

  4. public으로 열려있어 어느 계층에서든 접근 가능

  5. 사용하면 객체의 필드 값이 달라질 수 있다.

즉, 수정될 수 있다.

수정 되면 안되나...?

바뀌면 안되는 이유는 바로 DB에 접근을 하기 위한 데이터이기 때문이다.

예를 들어,
A. 화면 -> Controller -> Service -> Repository -> DB
로 이동하면서 다음과 같은 의문을 가질 수 있다.
1. 사용자에게 입력 받은 값이 맞나? Setter가 있는데?
2. DB는 생각한다. 이 데이터가 진짜 DB에 들어갈 수 있는거 맞아?
이 데이터가 무결성을 지킬 수 있나?
중간에 로직 탔는데, 무결성 지킬 수 있는거 맞아?

B. DB -> Repository -> Service -> Controller -> 화면
위 1,2가 이유
1. Setter가 있어서 중간 과정에서 무결성 상태가 지켜질 수 있을까?
진짜 DB에서 나온 Data라고 믿을 수 있을까?

궁금한 점

  1. Entity(Domain Object)에 Setter를 구현하지 않으면 되지 않나?

내 생각

  1. 객체에 Setter를 구현하지 않고, 생성자로 객체를 생성하여 반환해도 될 것 같다. 그러나, 다음과 같은 상황에서 Setter를 사용할 필요가 생길 수 있다.

DB의 데이터 수정이 필요할 경우, DB의 데이터를 Entity 타입으로 조회하여 가져온 후, 그에 대한 객체를 Setter로 수정한 후 다시 DB에 전달할 때

이럴 경우에 Entity에 특정 필드에 대한 Setter를 구현해 놓으면,
새로운 객체를 생성할 필요 없이 Setter를 통해 기존에 가져온 데이터를 수정한 후 전달하는 방법이 있다.

DTO와 Entity(Domain Object)

  • DTO: Domain Object의 사본 + "추가 필드" / "필드 제외"
    Setter는 DTO만 가지도록 하는게 무결성에 안전.

    역할

    1. 계산된 데이터
    2. 중간 중간에 데이터 변환도 일어날 수 있다.
      => Setter를 가질 수 있다.
  • Entity(Domain Object): DB의 원본 역할
    테이블과 1대1로 Mapping 되는 객체

    Entity는 Setter를 가지면 안될까?
    Setter를 가져도 된다.
    대신에 진짜 필요한 Setter인지 집중.
    그리고 수정 용도로만 사용하는 Setter 인지 확인.

참고자료
DTO, Entity

궁금한 점

  1. 수정 용도로만 Setter가 있어야 한다는데, 그것이 무슨 말 일까?

  2. DB Table Join 연산이 일어나면,,, DTO를 어떻게 정의해야 할까?

내 생각

  1. 다음과 같은 상황이다.
    DB의 데이터 수정이 필요할 경우, DB의 데이터를 Entity 타입으로 조회하여 가져온 후, 그에 대한 객체를 Setter로 수정한 후 다시 DB에 전달할 때

이럴 경우에 Entity에 특정 필드에 대한 Setter를 구현해 놓으면,
새로운 객체를 생성할 필요 없이 Setter를 통해 기존에 가져온 데이터를 수정한 후 전달하는 방법이 있다.

  1. 두 가지 방법이 있을 것 같다.
    1) JPA를 통해, SQL로 JOIN 연산을 통해 응답 받은 데이터를 DTO 객체를 만들어서 전달
    2) JPA를 통해, SQL로 JOIN 연산을 통해 응답 받은 데이터를 각 테이블에 맞는 Entity에 저장 후, Service 로직에서 Entity를 DTO 객체에 합쳐서 저장 후, Controller로 반환

나는 2)이라고 생각한다.
그 이유는 관심사의 분리 때문이다.
Entity는 DB에 1대1로 Mapping 되어야 한다고 생각하는데
DB로 부터 전달받은 JOIN 연산 데이터를 바로 DTO에 저장하는 방식은
Entity와 DTO가 만들어진 이유에 부합하지 않는다고 생각한다.
따라서,
JOIN 연산에 대한 데이터를 각 테이블에 따른 Entity에 저장 한 후에
Service에서 그에 대한 데이터를 합쳐서 DTO로 저장하여
Controller로 반환하는 방법이 좋다는 생각이 든다.

참고자료
https://mangkyu.tistory.com/192

어디까지 DTO이고, 어디까지 Entity 일까?

지금까지 배운 Spring의 구조도이다.

DTO와 Entity를 배우며 다음과 같은 질문이 떠오른다.

어디 계층까지 DTO가 담당하고, 어디 계층까지 Entity가 담당하게 할까?
언제 DTO -> Entity로, Entity -> DTO로 변환할까?

이에 대한 해답으로 아래 3가지 보기가 있다.

  1. DTO를 받아서, 바로 Controller에서 변환 후 Service 계층으로 전송한다.

  2. DTO를 Service로 보내고, Service에서 Entity로 변환한다.
    2.1 서로 before
    2.2 서로 after

  3. Repository에서 변환한다.

내 생각

나는 2번 방법을 선택할 것 같다.
그 이유는 관심사의 분리(SoC) 때문이다.
관심사의 분리란 서로 다른 관심사들을 분리하여 변경 가능성을 최소화하고, 유연하며 확장가능한 시스템을 만드는 것이다.

위에서 배웠던 DTO와 Entity에 대해 다시 정리해보자.

DTO
: DTO의 역할은 Entity의 사본으로써 객체에 대해 계산하는 것이다.

Entity
: Entity의 역할은 1대1과 Mapping 되는 객체이다.

Controller에서 DTO를 Entity로 변환하면 어떻게 될까?
Entity로 바뀐채로 Service로 보내면, Service에서 로직을 담당하는데
Entity를 수정해야 할 가능성이 생기므로 Controller는 아니라고 생각한다.

Service에서 비즈니스 로직을 처리하기 전에 변환을 한다?
비즈니스 로직 중에 데이터의 변환이 있을 수 있기에
Entity로 비즈니스 로직 처리 전에 변환하는 것은 좋지 않다고 생각한다.

내 생각은 Service에서 비즈니스 로직을 처리한 후에 DTO를 Entity로 변환 후 Respository로 전달한다.

그러면, Repository에서는 어떨까?
Repository의 목적은 DB와의 소통이 목적이다.
따라서, Entity 변환까지는 담당시키지 않는게 좋을 것 같다.

DTO <-> Entity 변환하는 방법

  1. 생성자
  2. Setter
  3. CopyProperties
  4. MapStruct
  • ModelStruct

기본 생성자 + Setter

참고자료
https://lucas-owner.tistory.com/19

구현

  1. DTO를 Entity로 변환
  2. Entity를 DTO로 변환

위 과정을 구현하려면 어떤 순서로 진행해야 할까?

내 생각

  1. RequestDTO, ResponseDTO 클래스를 정의 한다.
    1.1 데이터를 전달시 사용할 필드를 정의한다.
    1.2 데이터 연산을 위한 메서드를 정의한다.
    1.3 필드를 조회하고 수정할 Getter, Setter를 정의한다.

강사님
1. DTO 클래스 생성
- 필드(추가, 삭제 고려) 준비
2. 변환하는 방법에는 어떤 게 있을까?
- 구글링
3. 원하는 위치에서 구현

주문하기(=주문 생성) API

  1. API 설계 - 명세서에 추가 (URL, method...)

    주문 1개 당 상품 1개 주문할 수 있도록

    1.1 Request Body

    {
    	String product,
        int quantity
    }

    1.2 Response Body

    {
    	int id
    }
  2. 클래스 기본 구현
    2.1 Domain Object 구현
    2.2 Controller 구현
    2.3 Service 구현
    2.4 Repository

  3. 주문하기 구현

배운 개념
1. DTO의 등장
Order class와 Request Parameter가 다를 경우

----- DTO 등장 전 ------

  1. id, count -> order object..?
  2. dto 끼운다.
    =>
    방법1. dto(id, count) -> order object (entity)
    방법2. id, count -> dto (product) -> order (entity)
    • 변환 방법 : 생성자

궁금한 점
1. 왜 url을 /order말고 /orders로 하였을까?
2. DTO로 전달받은 product ID로 어느 계층에서 Product를 불러올까?
아래 파사드 패턴을 사용하여 해결하는 것을 정리해놓았다.

다른 도메인의 id로 객체 꺼내기

Order 도메인에서
Product 도메인의 데이터를 가져오려고 한다.

가져오는 방법은 다음과 같이 4가지 방법이 있을 것이다.

  1. OrderController -> ProductController -> ProductService -> ProductRepository
  2. OrderController -> ProductService -> ProductRepository
  3. OrderService -> ProductService -> ProductRepository
  4. OrderService -> ProductRepository
  • 내 생각
    2번
    OrderController에서 ProductService의 서비스 계층에 데이터를 요청하고, Service 계층에서 Repository에 요청을 한 후 받아오는 방법이 맞을 것 같다.
    3번의 경우 같은 Service 계층에서 요청할 경우 종속성이 생길 것 같고, 4번의 경우 Service <-> Repository에서 데이터를 주고 받을 때 Dto <-> Entity의 변환이 필요한데, OrderService에서 ProductRepository에 데이터를 요청하려면 어떤 DTO를 어떤 Entity로 변환시켜 요청해야 할지 잘 모르겠다.

강사님 생각

Order
Controller -> Service -> Repository

Product
Controller -> Service -> Repository

  1. 같은 계층끼리는 분류해줘야 한다.
    OrderController가 ProductController를 사용하는 거는 종속성이 발생한다.

Controller의 역할은 Service를 사용하는 것이다.

그러므로, 1번과 3번 방법은 객체지향적으로 봤을 때, 지양하는 편이다.

  1. Service는 다른 도메인의 Repository를 사용할 수 있다.

예를 들어 ProductRepository에서 조회를 하려고 한다.
OrderController -> ProductRepository 까지 가려면
어떻게 해야할까?

OrderService에서 ProductRepository로 데이터를 조회할 때,
유효성 검사를 하려면 어디서 해야할까?
유효성 검사는 로직이므로 Service 계층에서 진행할 것 이다.

등록에 대한 로직을 생각해보자.
ProductRepository에 대한 데이터의 유효성 검사를 OrderService에서 하려면 유효성 검사를 OrderService에서 또 구현해줘야 한다.

트랜잭션의 경우도 Service에서 처리한다.

OrderController -> ProductService -> ProductRepository의 경우
OrderController가 알아야하는 Service가 많다.
라는 문제가 생긴다.

해결방법은 파사드 패턴 이다.
파사드 패턴은 여러 도메인의 서비스를 공부하는 것을 만들어 놓고,
그 공부한 거를 따르는 것 이다.

참고 자료
파사드 패턴
Spring 파사드 패턴

MSA vs Monolithic

내가 진짜 이해하면서 하는지
내가 외워가면서 개발하지는 않는지
그거 물어보는거예요
처음 들어보는 사람에게 이해되게 설명할 수 있게 준비

  • 개념

    • 차이점
    • 장/단점?
  • 적용할 수 있는 프로젝트 예시

  • 개념
    MSA (Micro Service Architecture)
    : 서버를 각 도메인 별로 나눠서 하나의 서비스를 구현하는 구조

    Monolithic
    : 하나의 서버로 하나의 서비스를 구현하는 구조

  • 차이점

    • Monolithic은 한 곳에서 에러가 발생하면 서비스 전체에 장애가 발생하나, MSA는 한 곳에서 에러가 발생하여도 해당 도메인만 문제가 발생하고 전체 서비스를 구동시킬 수 있다.
    • Monolithic은 Scale Out을 하려면 전체 서비스를 옮겨야 하여 서버를 구현하는데 비용이 많이 드나, MSA는 성능을 올려야 하는 기능만 추가적으로 서버를 추가하면 되므로 확장에 용이하다.
  • 장/단점
    Monolithic

    • 장점
      • 프로토타입 서비스를 만들기에 용이하다.
    • 단점
      • 부분 장애가 전체 서비스의 장애로 확대될 가능성이 있다. 
      • 잘못된 코드 배포 또는 갑작스런 트래픽 증가로 인해 성능에 문제가 생겼을 때, 서비스 전체의 장애로 확대될 가능성이 있다.

    MSA

    • 장점
      • Scale out이 용이하다.
    • 단점
      • 구조를 구성하기에 복잡하다.
      • 분산 트랜잭션을 처리가 어렵다.
  • 적용할 수 있는 프로젝트

    • MTS 프로젝트에 용이할 것 같다는 생각이 든다. IPO 같은 대규모 트래픽을 처리해야 할 이벤트가 있을 경우, 해당 일자에 맞춰 Scale out 하여 서버를 구성하는 비용을 감소할 수 있을 것 같다.

참고자료
MAS란 무엇인가
Scale up과 Scale out
마이크로서비스간 통신시 주의할 점
kafka와 saga pattern

알쓸송잡
	1. 발표
    * 시선 처리
    * 말투 (ex> 이제,, 사실,, 끝을 흐리시는 분..)
    
    증권사 백엔드
    1. 대용량 데이터 처리 어떻게 할지?
    
    라이브러리 vs 프레임워크
    1. React는 라이브러리인가 프레임워크 인가?
    : 프레임 워크는 해당 기술로 프로젝트를 완성시킬 수 있어야 한다고 생각한다. 따라서, Spring은 프레임워크 ,React는 라이브러리라고 생각한다.
    
    인스타그램 대용량 알람 시스템
    1. 제니가 피드를 올린다.
    2. 인스타그램은 죽어 있던 서버를 킨다.
    3. 그 위에 Redis를 얹은다.
    4. 만반의 준비를 하고 오픈 시킨다.
    
    
    
    
    

😉 앞으로 이걸 해봐야지

Entity와 DTO의 개념에 대해 배우고, 개념을 코드에 적용해보았다. 앞으로 Spring으로 클라이언트와 데이터를 주고 받을 때 DTO를 정의하여 주고 받고, 이를 연산하여 Entity에 변환하여 DB에 저장할 것 이다.
그리고 MSA 구조를 통해 확장에 용이하고 서비스 장애에 유연한 서버를 개발해볼 것 이다.

profile
dev

0개의 댓글