Notice
Recent Posts
Recent Comments
Link
«   2026/07   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
Tags
more
Archives
Today
Total
관리 메뉴

어리바리 신입 개발자의 얼렁뚱땅 개발 기록 ✨

23.08.26 / [Java / Springboot] DTO(Data Transfer Object) / 계층화 아키텍처(Layered Architecture) 본문

TEAM PROJECT

23.08.26 / [Java / Springboot] DTO(Data Transfer Object) / 계층화 아키텍처(Layered Architecture)

낫쏘링 2023. 8. 26. 21:11
728x90

[ DTO (Data Transfer Ojbect) ]

  • DTO(Data Transfer Object) : 계층 간 데이터 전송을 위해 도메인 모델 대신 사용되는 객체( View <- DTO -> Controller )
  • 도메인 모델 : 소프트웨어의 주요 비즈니스 로직과 관련된 데이터와 동작을 표현하는 객체나 개념들의 모음
  • 오직 데이터를 저장하고, 데이터에 대한 getter, setter 용으로만 사용되어야 한다.
  • 어떠한 비즈니스 로직을 가져서는 안된다.
  • 비즈니스 로직(도메인 로직, 애플리케이션 로직) : 프로그램의 핵심 로직, 사용자의 눈에는 보이지 않지만 데이터가 생성, 저장, 수정되는 방법을 정의한 것
  • 저장, 검색, 직렬화역직렬화와 관련된 로직만을 가져야 한다.
  • 직렬화 :  DTO를 Byte, Json, Xml 등의 형태로 변환하는 것
  • 역직렬화  Byte, Json, Xml 등을 DTO 형태로 변환하는 것

 

왜 굳이 도메인 모델 대신 DTO를 사용할까?
도메인 모델은 중요한 로직과 데이터를 갖고 있다. 
그런데 도메인 모델을 계층 간 데이터 전송에 사용하게 되면, 
UI 계층에서 도메인 모델의 메서드를 호출하거나 상태를 변경시킬 수 있다.
이런 방식으로 도메인 모델의 내부 정보가 외부에 노출되면 보안 문제가 발생할 수 있다.
때문에 도메인 모델은 캡슐화하여 외부에서 변경할 수 없게 해야 한다.
또한 모델과 뷰의 결합도가 약해야하는 MVC 패턴에서 모델과 뷰의 결합도를 강하게 만들 수 있다.
즉, 도메인 모델 대신 DTO를 계층 간 데이터 전송에 사용함으로써 도메인 모델을 캡슐화하고 모델과 뷰의 결합도를 약하게 만들 수 있다. (Service에서 도메인 모델을 DTO로 변환하여 Controller로 전달한다.)

 

[ 계층화 아키텍처(Layered Architecture) ]
- 각 계층은 서로 다른 특정한 역할을 수행해야 한다.
- 적게는 2계층, 많계는 5계층 까지 나눌 수 있다.


1. Presentation Layer(표현 계층) : View, Controller
   - 애플리케이션으로 들어오는 요청(Request)와 응답(Response)을 전달 / 반환한다.
   - 클라이언트로 부턴 받은 JSON과 XML 형태의 데이터 구조 요청에 대해서 처리 후 응답한다.
   - 입력 데이터의 검증 및 적절한 에러 응답 처리

2. Business Layer(비즈니스 계층) : Service 
   - 로직을 기준으로 처리하여 영속 계층으로 전달
   - 고객의 요구사항을 반영한다.
   - 예전에는 인터페이스를 이용해 결합을 약하게 만들어야 했다.
     하지만, 대부분의 프로젝트에서는 인터페이스와 구현 클래스 사이의 관계가 1:1의 관계로 구성되기 때문에 굳이 인터페이스를 사용하지 않아도 된다.
   - 표현 계층과 영속 계층의 중간 다리 역할을 함으로써 도메인을 보호한다. (표현 계층에 도메인 노출 x)
   - 따라서, 비즈니스 계층에서 도메인 모델이 DTO로 변환되어 표현 계층(Controller)로 전달되어야 한다.
   - 표현 계층으로 부터 받은 DTO는 도메인 모델로 변환 후 Service에서 로직을 수행한다.
   - 즉, 비즈니스 계층에서 영속 계층으로 전달할 때는 도메인 모델로 전달 한다.(로직 수행 후 DTO로 변환 후 영속 계층까지 전달하기도 하지만 DTO가 영속 계층까지 전달되는 것은 지양한다. - 도메인 로직 분리와 유지보수 위함)
   - 단, Service와 DTO에서 직접적으로 DTO와 도메인 모델을 변환하는 로직을 수행하지 않고, Mapper 객체를 통해 수행한다. (mybatis의 mapper랑 다른 것임)

3. Persistence Layer(영속 계층) : DAO, Repository, mapper(mybatis)
    - 데이터베이스와 상호작용하여 데이터 CRUD 연산을 수행한다.
    - 데이터에 대한 영속성을 유지 ( 영속성 : 애플리케이션이 종료되어도 데이터가 유지되도록 한다.)
    - Repository : 도메인 중심적인 작업 수행 중점
    - DAO : 데이터베이스에서 CRUD 연산 작업 수행 중점 (mybatis에서는 mapper를 사용한다.)

 

[ DTO 형태 ]

private로 선언된 필드 / public으로 선언된 getter, setter 메서드 / toString

public class Point {
	private String userId;
	private int currentHoldingPoint;

	public String getUserId() {
		return userId;
	}
	public void setUserId(String userId) {
		this.userId = userId;
	}
	public int getCurrentHoldingPoint() {
		return currentHoldingPoint;
	}
	public void setCurrentHoldingPoint(int currentHoldingPoint) {
		this.currentHoldingPoint = currentHoldingPoint;
	}
	
	@Override
	public String toString() {
		StringBuilder builder = new StringBuilder();
		builder.append("Point [userId=");
		builder.append(userId);
		builder.append(", currentHoldingPoint=");
		builder.append(currentHoldingPoint);
		builder.append("]");
		return builder.toString();
	}

}
[ toString ? ]
- 일단, 셋 다 문자열을 저장하고 관리하는 기능을 한다. (java에서 제공하는 문자열을 다루는 자료형 class)
- DTO에 toString을 오버라이드하지 않으면 아무리 콘솔에서 DTO에 담긴 값을 확인하고 싶어도 알 수 없다.
- DTO는 결국 클래스이기 때문에 클래스를 콘솔에 찍으면 클래스의 주소값이 찍히기 때문이다.
- 즉, 디버깅을 위해서 toString 메서드를 오버라이드 한다고 생각하면 된다.

[ String / StringBuilder / StingBuffer의 차이점 ]
- String : String 객체는 한 번 생성되면 할당된 메모리 공간이 변하지 않는다.
              즉, 데이터가 바뀔 때 마다 객체가 새롭게 생성되고 메모리 공간도 새롭게 할당된다. 
              => 간단하고 직관적이지만 문자열이 많거나 자주 바뀌는 경우에 사용하지 않는 것이 좋다.
                    하지만 간단하고 동기화에 대해 신경쓰지 않아도 되기때문에
                     데이터가 자주 바뀌지 않고 데이터가 적을 때 사용할 수 있다.
@Override
public String toString() {
    return "Point{" +
        "userId='" + userId + '\'' +
        ", currentHoldingPoint=" + currentHoldingPoint +
        '}';
}

-  StringBuilder/StringBuffer : .append() .delete() 등의 API를 이용해서 이미 생성된 객체 내에서 수정이 가능하다.
                                               => 문자열이 자주 바뀌거나 많은 경우 사용할 수 있다.
                                              단, StringBuffer는 동기화 키워드를 지원하여 멀티쓰레드 환경에서 안전하고,
                                              StringBuilder는 동기화를 지원하지 않기 때문에 단일쓰레드에서 사용한다.
                                              단일쓰레드에서 사용할 경우 StringBuffer보다 StringBuilder의 성능이 더 뛰어나기
                                              때문에 쓰레드를 고려해서 사용해야 한다.

728x90