DTO, DAO (Spring 개념)

DTO 

데이터가 각 레이어를 거치는 과정에서 값이 변경되지 않도록, DTO 객체를 생성해서 값을 캡슐화하여 전달한다.

DTO를 사용하면 캡슐화를 통해 외부 접근을 방지하는 것 뿐만 아니라, DB에서 가져온 데이터들을 하나의 객체로 묶어서 다른 레이어로 전달할 수 있다.

그리고 DB 구조가 바뀌는 상황에서 프로그램의 전체 API를 수정하지 않고, DTO 구조만 수정하면 된다는 장점이 있다.

 

DAO 

DB에 접근하는 "객체"이기 때문에 DAO(Data Access Object)라고 표현한다.

DAO는 DB와 상호작용하는 로직만 수행하는 객체, Repository는 그것을 객체 지향적으로 구현한 형태

 

  • 객체 지향적 설계 - 메서드로 상호작용을 구현하여, DB 접근 캡슐화

 

 

+ Domain

데이터 + 비즈니스 로직을 담은 객체

  • DB에 저장되는 객체들을 구현한 것으로, DTO와 혼동할 수 있지만 DTO는 비즈니스 로직을 포함할 수 없다.
  • 즉, Domain이 DTO의 역할을 하지만, DTO가 Domain을 대체할 수 있는 것은 아니다.

Repository는 DB에 직접적으로 접근해 도메인 객체를 DB에 저장하고 관리한다.

// 도메인(Entity)에서 비즈니스 로직을 수행

@Entity
public class User {
    @Id @GeneratedValue
    private Long id;
    private String name;

    public void changeName(String newName) {
        this.name = newName; // 비즈니스 로직 (상태 변경)
    }
}
// Repository는 데이터 저장 & 조회만 담당

public interface UserRepository extends JpaRepository<User, Long> {
    Optional<User> findByName(String name);
    ...
}
// 서비스(Service) 레이어에서 비즈니스 로직 호출

@Service
public class UserService {
    private final UserRepository userRepository;

    public UserService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public void updateUserName(Long userId, String newName) {
        User user = userRepository.findById(userId)
                .orElseThrow(() -> new IllegalArgumentException("사용자 없음"));

        user.changeName(newName);  // 비즈니스 로직 실행 (도메인 객체 메서드 호출)
        userRepository.save(user); // 변경된 데이터 저장 (레포지토리 메서드 호출)
    }
}

 


@Transactional (Spring annotation)

메서드나 클래스에 적용하여, 이 단위가 완전하게 실행되거나 완전히 롤백되도록 보장 (트랜잭션 자동 관리)

기본적으로 런타임 예외(unchecked exception)와 Error는 롤백 O, 체크 예외(checked exception)는 롤백 X

  • 체크 예외도 롤백되게 하려면 rollbackFor 속성을 사용
DB에서 롤백이 필요한 상황 예시

예시 1. 데이터베이스 제약 조건 위반 (DataIntegrityViolationException)
ex. 회원가입에서 사용할 이메일을 DB에 저장하는 과정에서 DB에 이미 동일한 이메일이 존재하는 경우, 앞서 저장된 데이터들은 롤백이 필요하다. (UNIQUE 제약 조건 위반)

예시 2. 파일 업로드 후 DB 저장 실패 (SQLException)
ex. 파일이 정상적으로 DB에 업로드됐지만 파일의 메타데이터를 저장하는 과정에서 문제가 생길 수 있어, 파일 업로드의 롤백이 필요하다.

예시 3. 비즈니스 로직에서의 체크 예외 처리 (InsufficientStockException)
ex. 상품 재고가 부족한 상태에서 주문 데이터만 먼저 기록되고, 재고 수량을 수정하는 작업이 실패하면, 주문은 생성되었지만 실제 재고 상태와 일치하지 않게 되므로 주문에 대해 롤백이 필요하다.

propagation 속성값

@Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.SERIALIZABLE, timeout = 30, rollbackFor = Exception.class)
public void someMethod() {
    // 트랜잭션 작업
}

 


void와 Void의 차이점 (반환 타입)

void

반환값이 없는 메서드의 반환 타입

 

Void (대문자)

void 타입을 객체로 표현할 수 있도록 하는 자바의 래퍼 클래스이며, 주로 제네릭에서 사용한다.

Optional<T> 역시 null을 객체에 담아 전달할 수 있지만, null이 아닌 값이 올 수도 있다. (슈뢰딩거의 고양이?)

public ResponseEntity<Void> deleteMemo(@PathVariable Long id){

        memoService.deleteMemo(id);

        return new ResponseEntity<>(HttpStatus.OK);
}

orElse 메서드 (Optional 클래스의 메서드)

Optional 객체에 값이 존재하면 그 값을 반환하고, 값이 없으면 orElse(defaultValue) 메서드에 지정된 디폴트값을 반환한다.

Optional<T> optionalValue = Optional.ofNullable(value);
T result = optionalValue.orElse(defaultValue);

 

만약 값이 없을 때 예외를 던지고 싶다면 orElseThrow() 메서드를 사용한다.

String value = null;
Optional<String> optionalValue = Optional.ofNullable(value);

// 값이 없으면 예외를 던짐
String result = optionalValue.orElseThrow(() -> new IllegalArgumentException("Value is required"));

 


가변 인자

메서드의 파라미터 개수를 가변적으로 받을 때 사용한다.

public Menu addMenuItem(MenuItem… menuItem) // ...이 가변 인자로 받겠다는 의미

 

적절한 활용 예시

각 menu menuItem들을 add하려고 , 삽입할 데이터의 개수가 몇개든 한 번에 add 가능

List<MenuItem> menu = new AraryList<>();

public void addMenuItems(MenuItem... menuItem){
    menu.addAll(Arrays.asList(menuItem));
    
    return this;
}
List<Menu> menu = new Array.asList(
	new Menu("햄버거").addMenuItems(
    		new MenuItem("A버거", 1000원),
        	new MenuItem("B버거", 2000원),
        	new MenuItem("C버거", 3000원)
        ),
        new Menu("음료").addMenuItems(
    		new MenuItem("A음료", 1000원),
        	new MenuItem("B음료", 2000원),
        	new MenuItem("C음료", 3000원)
        )
    );

 

단점

메서드 오버로딩 불가

public void foo(int... nums) { }
public void foo(int num) { }
public void foo(int num1, int num2)   // 컴파일러가 어떤 메서드를 호출할지 혼동