본문 바로가기

Back-end & Server/Spring Boot with Kotlin

[Spring Boot] DAO, DTO

728x90
반응형

 

DAO(Data Access Object)

  • DB의 데이터에 접근하기 위한 객체이다.
  • DB에 접근하여 데이터를 조회하거나 조작하는 로직을 담당한다.
  • DB의 연결관리, SQL문 실행 등의 기능을 통해 데이터를 처리한다.
  • 일반적으로 인터페이스로 처리되고 이 인터페이스의 구현체에서 실제 DB 연결 및 데이터 처리 로직을 구현한다.
  • 이를 통해 데이터 접근 로직과 비즈니스 로직을 분리하여, 코드의 유지보수성을 높일 수 있다.

 

 

DTO(Data Transfer Object)

  • 계층 간 데이터 교환을 위해 사용되는 객체이다.
  • 일반적으로 DB에서 데이터를 가져온 후, 이를 클라이언트에게 전송하기 위해 사용되는 객체를 의미한다.
  • 로직을 가지지 않은 순수한 데이터 객체, getter, setter 메소드만 포함한다.
  • 데이터 전송을 목적으로 한다.

 

 

Spring Boot에서는 DI(Dependency Injection) 기능을 통해 DAO나 다른 비즈니스 서비스를 필요한 곳에 자동으로 주입하여 사용할 수 있다.

 

DTO와 DAO의 차이점

구분 DTO DAO
목적 데이터 전달 DB 접근, 조회, 조작
계층 서비스 레이어와 프레젠테이션 레이어 사이의 데이터 전송. DB와의 상호 작용
포함하는 내용 로직을 포함하지 않는 순수 데이터 구조체 DB 접근 로직을 포함

 

 

DTO의 사용이유

 

1. 계층 간 데이터 전송 최적화

  • 다양한 계층(ex. Controller, Service, View) 사이의 데이터 전송을 위해 사용한다.
  • 필요한 데이터만을 선별하여 전송할 수 있어, 네트워크 사용량을 줄이고 성능 최적화할 수 있다.

 

2. 안전성

  • 클라이언트에 노출되는 데이터를 제어할 수 있기 때문에, 민감한 정보의 노출을 방지할 수 있다.

 

3. 유연성

  • 클라이언트의 요구사항이 변경되어도 DTO만 수정하면 되므로, Service 또는 DB 계층에는 영향을 미치지 않고 유연하게 대응할 수 있다.

 

 

DAO의 사용이유

 

1. 데이터 접근 추상화 및 캡슐화

  • 접근 로직을 캡슐화하여, DB와의 상호작용을 단순화한다.
  • DB연결, SQL 쿼리 실행 등의 복잡한 절차를 추상화하여 사용할 수 있다.

 

2. 유지보수성 향샹

  • DB 접근 로직을 분리할 수 있어, Application의 다른 부분과 독립적으로 DB 접근 코드를 개선하거나 수정할 수 있다.

 

3. 재사용성 및 확장성

  • 일관된 인터페이스를 제공하는 것으로 DB의 작업을 재사용하기 쉽고, 필요에 따라 다른 DB로의 확장이나 변경이 용이해진다.

 

4. 분리된 비즈니스 로직과 데이터 액세스 로직

  • 비즈니스 로직과 데이터 액세스 로직의 분리를 가능하게 한다.
  • 이로 인해 각각의 로직에 집중할 수 있어, 더 깔끔하고 관리하기 쉬운 코드를 작성할 수 있다.

 

 

DTO와 DAO 사용의 선택

 

DTO 사용

  • 계층간 데이터 전송을 최적화하고, 클라이언트에게 전달되는 데이터 구조를 명확히 정의할 필요가 있을 때.
  • ex. API를 통해 외부 시스템과 데이터를 주고 받을 때

 

DAO 사용

  • DB와의 상호작용을 캡슐화하고, 상호작용을 재사용 가능하게 만들어야할 때.
  • DB 연산을 수행하는 코드를 여러 곳에서 반복적으로 사용해야 할 경우.
  • ex. DB 연산이 중복이 많은 경우

 

 

어플리케이션의 복잡성

  • 복잡하고 큰 프로젝트에서는 DTO, DAO를 사용하는 것이 구조의 명확성과 유지보수성을 위해 매우 유용할 수 있다.

 

데이터 처리 요구사항

  • 데이터를 다루는 방식이 복잡하거나, 다양한 데이터 소스를 사용하는 경우 유용하다.
  • 간단한 CRUD 작업만 필요한 경우 JPA Repository 사용만으로 충분할 수 있다.

 

프로젝트의 규모와 팀의 선호도

  • 프로젝트 규모가 작고, 단순한 어플리케이션을 빠르게 개발해야 하는 경우, DTO, DAO의 분리 없이 직접적인 모델 사용을 통한 개발이 더 효율적일 수 있다.
  • 팀이 코드의 명확성과 재사용성을 중시한 다면, DTO, DAO를 사용하는 구조를 선호할 수 있다.

 

 

DAO, DTO 사용 예시 - 사용자 정보 관리

1. Dependencies 추가

  • build.gradle에 JPA와 H2 Data base 의존성을 추가한다.
  • H2는 인메모리 DB로, 복잡한 설정 없이 간단한 테스트나 예제를 위해 사용하기 좋다.

 

2. Entity 생성

package com.springboot.kotlinexample.entity

// spring boot 3에서는 Jakarta EE 9가 포함됨에 따라 javax 관련 패키지명이 jakarta로 변경
import jakarta.persistence.Entity
import jakarta.persistence.GeneratedValue
import jakarta.persistence.GenerationType
import jakarta.persistence.Id
import jakarta.persistence.Table


@Entity
@Table(name="`user`")
class User(
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    var id: Long? = null,
    var name: String="",
    var email: String=""
)

 

3. DAO(Repository) 생성

package com.springboot.kotlinexample.dao

import com.springboot.kotlinexample.entity.User
import org.springframework.data.jpa.repository.JpaRepository

interface UserRepository : JpaRepository<User, Long>

 

 

4. DTO 생성

package com.springboot.kotlinexample.dto

data class UserDto(
    val name: String,
    val email: String
)

 

5. Service 계층 생성

package com.springboot.kotlinexample.service

import com.springboot.kotlinexample.dto.UserDto
import com.springboot.kotlinexample.dao.UserRepository

import org.springframework.stereotype.Service

interface UserService {
    fun findAllUsers(): List<UserDto>
}

@Service
class UserServiceImpl(private val userRepository: UserRepository) : UserService {

    override fun findAllUsers(): List<UserDto> = userRepository.findAll().map { UserDto(it.name, it.email) }
}

 

6. 컨트롤러 생성

package com.springboot.kotlinexample.controller

import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RestController
import org.springframework.web.bind.annotation.RequestMapping

// dto
import com.springboot.kotlinexample.dto.UserDto
// service
import com.springboot.kotlinexample.service.UserService

@RestController
@RequestMapping("/users")
class UserController(private val userService: UserService) {

    @GetMapping
    fun getAllUsers(): List<UserDto> = userService.findAllUsers()
}

 

테스트를 위해 dataloader 추가

package com.springboot.kotlinexample.dataloader

import com.springboot.kotlinexample.entity.User
import com.springboot.kotlinexample.dao.UserRepository
import org.springframework.boot.CommandLineRunner
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration

@Configuration
class DataLoader {

    @Bean
    fun initDatabase(userRepository: UserRepository) = CommandLineRunner {
        userRepository.save(User(name="pupba1", email = "pupbani11@example.com"))
        userRepository.save(User(name="pupba2", email = "pupbani12@example.com"))
        userRepository.save(User(name="pupba3", email = "pupbani13@example.com"))
    }
}

 

728x90
반응형