본문 바로가기
SpringBoot Server/API

DAO, DTO 알아보기 // 예제 - 유저관리 API POST

by ssury94 2024. 12. 22.

DAO (Data Access Object) 란?

데이터베이스나 기타 영속성 메커니즘에 접근하는 객체
 

  • 목적: 데이터 접근 로직과 비즈니스 로직을 분리
  • 기능: CRUD(Create, Read, Update, Delete) 작업 수행
  • 사용이유: 
      - 코드 재사용성 향상
      - 데이터 접근 방식 변경 시 유연성 제공

 

DTO (Data Transfer Object)

프로세스 간 데이터를 전달하는 객체

  • 목적: 데이터 전송 최적화
  • 특징: 
    비즈니스 로직 없이 데이터만 포함
    - 일반적으로 단순한 getter/setter 메서드만 가짐
  • 장점:
    네트워크 호출 횟수 감소
    데이터 캡슐화


* DAO와 DTO의 관계
- DAO는 데이터베이스와 상호작용하여 데이터를 가져오고, 이를 DTO에 담아 비즈니스 계층으로 전달한다.
- DTO는 여러 계층 간 데이터 전송에 사용되며, DAO에서 검색한 데이터를 담는 컨테이너 역할을 한다.
 
데이터 접근과 전송을 효율적으로 관리하고, 애플리케이션의 구조를 더 명확하고 유지보수하기 쉽게 만들기 위함



유저 이름과 이메일을 관리하는 API만들기 예제 // todo 1 creatUser

1. 유저 명세서 확인

 


2. Postman에 API Request 작성

URL path는 명사로 작성합니다.( RESTful API 설계 원칙)

{
    "name" : "크리스",
    "email" : "122234a@naver.com"
}

 
Body - Json문으로 들어갑니다. 맞게 양식 작성하기
 


3. DB 만들기

 


4. entity 클래스 변수 선언

데이터베이스 테이블 컬럼을 참고하여 entity 유저 클래스 변수 선언
테이블이름과 클래스 이름은 똑같이
 

package com.marurun66.user2.entity;

public class User {

    private long id;
    private String name;
    private String email;
    private String createdAt; // not creatd_at

 
데이터타입이 맞다면
컬럼은 creat_at이지만 변수는 createdAt으로 잘 변환하여 저장해준다.= 네이밍컨벤션


5. Controller 클래스에 메서드 작성

처리할 컨트롤러 클래스 만들기
클래스가 컨트롤러 클래스임을 어노텐션
@RestfullController
createUser 메서드를 만들자.
 

//postman에서 리퀘스트 json문 확인하고 PostMapping 어노텐션 작성
@PostMapping("/api/users")
public createUser(@RequestBody User user){
// @RequestBody로 받아라 User 클래스의 user 변수
// db에 name, email 저장해라 >> db에서 처리할 담당 클래스 dao/userDAO 만들기

 
@PostMapping ("/path")

  • 용도: 주로 새로운 리소스를 생성하거나 데이터를 서버로 제출할 때 사용됩니다.
  • 사용 방법: 컨트롤러 클래스의 메서드에 @PostMapping 어노테이션을 붙여 사용합니다.
  • 요청 본문 처리: @RequestBody 어노테이션을 사용하여 JSON 형식의 요청 본문을 자바 객체로 변환받을 수 있습니다.
  • URL 매핑: @PostMapping("/path")와 같이 특정 경로에 대한 POST 요청을 처리할 수 있습니다.
  • 데이터 전송: HTML의 <form> 태그나 AJAX를 통해 POST 방식으로 데이터를 전송할 수 있습니다.
  • 반환 값: 메서드의 반환 값은 자동으로 JSON 형식으로 변환되어 응답 본문에 포함됩니다.

 


6. DB에 변수 name, email 데이터 저장할 처리할 담당 클래스 dao/userDAO 만들기

DAO (Data Access Object)
dao 패키지에 데이터베이스와 작업하는 UserDAO 클래스 생성
데이터베이스에 쿼리할 SQL을 실행하는 클래스
 

Package com.marurun66.user.dao;

import com.marurun66.user.entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

@Repository
public class UserDAO {
    @Autowired
    JdbcTemplate jdbcTemplate;
    //todo Post
    public int createUser(User user){
        String sql = "INSERT INTO user (name, email)\n" +
                "values (?,?);";
        return jdbcTemplate.update(sql, user.getName(),user.getEmail());
    }
}

 
@Respository

  • 데이터 접근 계층 표시: @Repository는 주로 DAO(Data Access Object) 클래스에 사용되어 해당 클래스가 데이터베이스와 상호작용하는 역할을 한다는 것을 나타냅니다.
  • 예외 변환: 이 어노테이션을 사용하면 데이터 액세스 기술과 관련된 예외를 Spring의 DataAccessException으로 자동 변환해줍니다.
  • 컴포넌트 스캔: @Repository는 @Component의 특수화된 형태로, 스프링의 컴포넌트 스캔 기능에 의해 자동으로 빈(스프링 컨테이너가 관리하는 자바 객체)으로 등록됩니다.
  • 트랜잭션 관리: @Repository가 붙은 클래스는 트랜잭션 관리 대상이 됩니다.

@Autowired

  • 자동 의존성 주입: @Autowired는 스프링 컨테이너에서 관리하는 빈(Bean) 객체를 자동으로 주입합니다.
  • 다양한 적용 위치: 필드, 생성자, 메서드(주로 setter)에 사용할 수 있습니다.
  • 타입 기반 주입: 주입하려는 객체의 타입을 기준으로 빈(스프링 컨테이너가 관리하는 자바 객체)을 찾아 주입합니다.
  • 여기서는 jdbcTemplate을 자동으로 주입받기위해 사용하였다.

 
DAO에서 일할 createUser() 메서드 작성
DAO는 SQL문을 작성하고, 그 SQL문을 실행하여 데이터베이스와 통신합니다
createUser(User user){
String sql = DB에 들어갈 인서트문: INSERT INTO(name,email) values ( "크리스", "***@mail.com")
크리스와 크리스의 email은 ? ? 로 작성해주면 자동 이스케이핑된다.
 
받은 값과 변수를 리턴
return jdbc.Template.update(sql, user.getname(), user.getemail());
set이 아니고 get인 이유
POST 요청으로 전달된 데이터를 기반으로 User 객체를 생성한 후, 데이터베이스에 저장하려면 객체 내부의 값을 읽어와야 합니다.
 
Template.update()는 sql문에 의해 영향받은 행의 개수 (int)로 리턴된다.
 
성공적으로 유저데이터가 추가되었을시 데이터테이블에 0 초과 (예시에서는 1명만 추가처리되지만, 여러 유저데이터를 받았을경우를 대비해서)
추가 실패땐 0이 반환 (음수인 경우는 거의없다.)
 
DAO에서 sql문 받아줄 createUser()작성했으니
다시 controller로 돌아가자
 


7. Controller 추가작성

// DAO 작성 전 작업하다 만 createuser()마저 완성하기

@PostMapping("/api/users")
public createUser(@RequestBody User user){

 
 
userDAO의 빈을 관리해줄 @Autowired 추가 

    @Autowired
    UserDAO userDAO;

 
userDAO의 createUser()는 DB에 새 사용자 정보 추가시 작업 결과로 영향받은 행의 개수를 반환받음 (int)
int result = userDAO.createUser(user);
result의 변수를 userDAO.createUser(user)
= userDAO클래스에서 createUser메소드가 user 객체 정보를 DB에 저장한 반환값으로 초기화
 
if (result > 0) {
return 데이터 전송 성공시 (=코드 400) status를 "success"으로
}else{
return 데이터 전송 실패시 (=코드 500) status를 "fail"로 반환
 
성공, 실패여부를 클라이언트가 관리(유저가 등록 시도했을때 DB에 등록이 잘 되었는지, 실패했다면 그 원인을 알수 있다면 좋겠죠)할 수 있도록, status 변수를 관리해줄 클래스를 하나 생성하겠습니다.


8. UserResDTO 클래스 생성 및 변수 선언

dto/UserResDTO

package com.marurun66.user.dto;

public class UserResDTO {
    public String status;

    public UserResDTO() {
    }

    public UserResDTO(String status) {
        this.status = status;
    }
}

 
성공 실패 여부 status를 필드로 사용하는 UserResDTO클래스
데이터 캡슐화를 위해 getter, setter도 지정


 

9. Controller 추추가작성

 

public class UserController {
    @Autowired
    UserDAO userDAO;

    //todo 1 createUser
    @PostMapping("/api/users")
    public ResponseEntity<UserResDTO> createUser(@RequestBody User user) {
        try {
            int result = userDAO.createUser(user);
            if (result > 0) {
                return ResponseEntity.status(200).body(new UserResDTO("success"));
            } else {
                return ResponseEntity.status(500).body(new UserResDTO("fail"));
            }
        } catch (Exception e) {
            return ResponseEntity.status(400).build();
        }
    }

if (result > 0) {
return 데이터 전송 성공시 (=코드 400) status를 "success"으로
}else{
return 데이터 전송 실패시 (=코드 500) status를 "fail"로 반환
}
 
즉,
if (result > 0){
return 데이터 전송 성공시 (=코드 400) status를 "success"으로
return
ResponseEntity = HTTP 응답을 나타내는 Spring Famework의 클래스
.status(200) = HTTP 상태코드가 200
.body(new UserResDTO("success")); = 새 UserResDTO 객체를 생성하고 "success"라는 문자열을 생성자에 전달하여 객체 초기화
 
}else{
 
return 데이터 전송 실패시 (=코드 500) status를 "fail"로 반환
return
ResponseEntity
.status(500) = HTTP 상태코드가 500
.body(new UserResDTO("fail")); =새 UserResDTO 객체를 생성하고 "fail"라는 문자열을 생성자에 전달하여 객체 초기화
 
 

HTTP 상태코드들

https://developer.mozilla.org/ko/docs/Web/HTTP/Status

 

HTTP 상태 코드 - HTTP | MDN

HTTP 응답 상태 코드는 특정 HTTP 요청이 성공적으로 완료되었는지 알려줍니다. 응답은 5개의 그룹으로 나누어집니다: 정보를 제공하는 응답, 성공적인 응답, 리다이렉트, 클라이언트 에러, 그리고

developer.mozilla.org

 


10. Try, catch(Exception e) 로 오류상황 대처
잘못된 문법으로 인하여 서버가 요청을 이해할 수 없는 400 코드일때 status에 상태코드만 설정하기
 
코드 500의 상황 -
서버가 처리 방법을 모르는 상황이 발생했습니다. 서버는 아직 처리 방법을 알 수 없습니다.
클라이언트 요청 자체는 정상적으로 처리가능하지만 (데이터 형식, 구조에는 문제가 없음)
unique키인 이메일에 중복데이터가 들어갔다던지 하는 비즈니스로직 (중복검사)에서 조건을 만족하지 못한상황
 
코드 400의 상황 -
Json문 형식 오류, 필수필드가 누락된 상황
즉, 리소스와의 충돌상태
 
try {
return
ResponseEntity..status(200).body(new UserResDTO("success"));
}else{
return ResponseEntity.status(500).body(new UserResDTO("fail")); 
}
catch(Exception e) {
return ResponseEntity.status(500)
.build();=응답 본문 없이 간단하게 오류인것만 전달
 
 
 
 
이제 createUser()메서드의 본문이 완성되었으니
메서드의 데이터타입과 public를 적어주면 작성 완료!
ResponseEntity<UserResDTO> = ResponseEntity 가 반환하는 응답 본문의 데이터 타입 ex) "success"를 포함한 객체
 
 

//todo 1 createUser
@PostMapping("/api/users")
public ResponseEntity<UserResDTO> createUser(@RequestBody User user) {
    try {
        int result = userDAO.createUser(user);
        if (result > 0) {
            return ResponseEntity.status(200).body(new UserResDTO("success"));
        } else {
            return ResponseEntity.status(500).body(new UserResDTO("fail"));
        }
    } catch (Exception e) {
        return ResponseEntity.status(400).build();
    }
}

 
 
 
 

 
크리스의 이메일이 성공적으로 등록되어 status : seccess로 리스폰받음