1. dto 설계 하기 및 개념 확인(사전 기반 지식)
🔹DTO 설계 및 개념 이해
Spring Boot 애플리케이션에서 DTO(Data Transfer Object)와 Model을 분리하는 것은 유지보수성과 가독성을 높이기 위해 중요한 설계 원칙 중 하나입니다.
✅ DTO와 Model의 차이점
구분 | DTO (Data Transfer Object) | Model (Entity, Domain Model) |
역할 | 클라이언트와 서버 간 데이터 전달 | DB 테이블과 매핑되는 비즈니스 객체 |
사용 목적 | View-Controller 간의 데이터 교환 | 데이터베이스 저장 및 조회 |
설계 방식 | 주로 @Getter와 @Setter만 포함 | JPA Entity, @Entity, @Table 사용 |
✅ DTO와 Model을 분리하는 이유
- 가독성과 유지보수성 향상:
- Model 클래스에는 비즈니스 로직이 포함될 수 있으므로, 이를 DTO와 분리하여 클라이언트와 데이터베이스를 각각 다룰 수 있습니다.
- 유지보수가 쉬워지고, Model이 변경될 때 API 요청 및 응답 데이터 구조를 변경하지 않아도 됩니다.
- 객체의 역할 구분:
- Model(Entity)은 데이터베이스 테이블과 매핑되는 객체이며, 데이터 저장을 위한 ORM 객체입니다.
- DTO는 API 응답 및 요청 데이터를 위한 데이터 전달용 객체입니다.
- 유연한 변경 가능:
- Model을 직접 노출하면 보안 이슈(불필요한 필드 포함)가 발생할 수 있음.
- API 요구사항이 변해도 Model을 변경할 필요 없이 DTO만 수정하면 됨.
🔹Spring Boot Web MVC 구조
Spring Boot 애플리케이션은 일반적으로 3-Tier Layered Architecture를 따릅니다.
✅ 레이어별 역할
- Web Layer (Controller)
- 클라이언트 요청을 받고 응답을 반환하는 역할.
- @RestController 또는 @Controller 사용.
- @RequestMapping을 활용하여 API 엔드포인트를 정의.
- Business Layer (Service)
- 내부 비즈니스 로직을 처리하는 계층.
- @Service 어노테이션 사용.
- 데이터 검증, 로직 처리, 트랜잭션 관리 수행.
- Data Layer (Repository, DAO)
- 데이터베이스와 직접 통신하는 계층.
- @Repository와 JPA, MyBatis 등을 사용하여 데이터 CRUD 처리.
- 외부 API와 연동할 경우, API Client를 정의하여 데이터를 가져옴.
📌 핵심 개념:
서비스(Service) 레이어를 활용하면 컨트롤러에서 비즈니스 로직을 분리할 수 있어 유지보수성이 향상됩니다.
또한, 컨트롤러가 단순히 요청을 처리하는 역할만 하도록 설계하면 **SRP(단일 책임 원칙)**을 준수할 수 있습니다.
🔹트랜잭션 개념과 ACID 원칙
트랜잭션(Transaction)은 데이터베이스에서 수행되는 일련의 작업 단위를 의미합니다.
즉, 하나의 트랜잭션 내에서 여러 SQL 작업이 실행되더라도 모두 성공해야 하고, 하나라도 실패하면 전체 작업이 롤백(rollback)되어야 합니다.
✅ ACID 원칙이란?
ACID는 데이터의 무결성과 안정성을 보장하기 위한 트랜잭션의 4가지 핵심 속성입니다.
- 원자성 (Atomicity)
- 트랜잭션은 All or Nothing 방식으로 실행됩니다.
- 예: 계좌 이체 시, 출금과 입금이 하나의 트랜잭션으로 실행되며 둘 중 하나라도 실패하면 전체가 롤백됩니다.
- 일관성 (Consistency)
- 트랜잭션이 실행되기 전과 후에 데이터가 항상 일관된 상태를 유지해야 합니다.
- 예: 은행 잔고 합계가 변경되면 안 됨.
- 고립성 (Isolation)
- 여러 트랜잭션이 동시에 실행될 때 서로 간섭하지 않도록 보장해야 합니다.
- 트랜잭션 격리 수준에 따라 성능과 일관성이 조정될 수 있습니다.
- 지속성 (Durability)
- 트랜잭션이 완료된 후에는 데이터가 영구적으로 저장됩니다.
- 서버가 다운되거나 장애가 발생해도 데이터가 손실되지 않음.
✅ Spring Boot에서 트랜잭션 관리
Spring Boot에서는 @Transactional 어노테이션을 활용하여 트랜잭션을 관리할 수 있습니다.
@Service
public class UserService {
@Transactional
public void registerUser(UserDTO userDTO) {
userRepository.save(userDTO.toEntity()); // 사용자 저장
sendWelcomeEmail(userDTO.getEmail()); // 환영 이메일 발송
}
}
📌 @Transactional을 적용하면 모든 작업이 성공적으로 완료될 때만 커밋(Commit)되고, 실패하면 자동으로 롤백(Rollback) 됩니다.
2. UserController, UserService 설계 및 유효성 검사, 예외 처리
SignUpDTO 클래스 설계
package com.tenco.bank.dto;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
// SignUpFormDTO
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class SignUpDTO {
private String username;
private String password;
private String fullname;
// ToDo 추후 진행 예정
// private MultipartFile customFile; // name 속성과 일치 시켜야 함
// private String originFileName;
// private String uploadFileName;
// private String eMail;
}
🔹SignUpDTO 설계
DTO는 클라이언트와 서버 간 데이터를 주고받기 위한 객체이며, 회원 가입 시 필요한 데이터를 담고 있습니다.
- @Data: Getter, Setter, toString 등을 자동 생성
- @NoArgsConstructor, @AllArgsConstructor: 기본 생성자 및 전체 필드 포함 생성자 자동 생성
- @Builder: 객체를 유연하게 생성할 수 있도록 지원
UserController 클래스 생성 및 구현
package com.tenco.bank.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import com.tenco.bank.dto.SignUpDTO;
import com.tenco.bank.handler.exception.DataDeliveryException;
import com.tenco.bank.service.UserService;
@Controller
@RequestMapping("/user") // 대문 처리
public class UserController {
// 1단계
@Autowired // DI 처리
private UserService userService;
// 2단계
//private final UserService userService;
// 2단계
// private final UserService userService
// final 처리를 하는 이유는 (성능향상)
// @Autowired 함께 표시 가능, 표시 하는 이유는 (가독성 향상)
public UserController(UserService userService) {
this.userService = userService;
}
/**
* 회원 가입 페이지 요청
* 주소 설계 http://localhost:8080/user/sign-up
* @return signUp.jsp 파일 리턴
*/
@GetMapping("/sign-up")
public String signUpPage() {
// prefix: /WEB-INF/view/
// suffix: .jsp
return "user/signUp";
}
// 회원 가입 요청 처리
// 주소 설계 http://localhost:8800/user/sign-up
// Get, Post -> sign-up 같은 도메인이라도 구분이 가능하다.
// REST API 를 사용하는 이유에 대해한번 더 살펴 보세요
@PostMapping("/sign-up")
public String signProc(SignUpDTO dto) {
// 1. 인증검사 x
// 2. 유효성 검사
if(dto.getUsername() == null || dto.getUsername().isEmpty()) {
throw new DataDeliveryException("username을 입력 하세요",
HttpStatus.BAD_REQUEST);
}
if(dto.getPassword() == null || dto.getPassword().isEmpty()) {
throw new DataDeliveryException("password을 입력 하세요",
HttpStatus.BAD_REQUEST);
}
if(dto.getFullname() == null || dto.getFullname().isEmpty()) {
throw new DataDeliveryException("fullname을 입력 하세요",
HttpStatus.BAD_REQUEST);
}
userService.createUser(dto);
// todo 로그인 페이지로 변경 예정
return "redirect:/user/sign-up";
}
}
🔹 UserController 설계
UserController는 회원 가입 관련 요청을 처리하는 컨트롤러입니다.
2.1 의존성 주입
- @Autowired를 사용하여 UserService를 주입할 수도 있지만,
- **생성자 주입 방식(권장됨)**을 사용하면 불변성을 보장하고 테스트가 용이합니다.
2.2 회원 가입 페이지 요청
- URL: GET /user/sign-up
- 처리: 회원 가입 페이지 (signUp.jsp) 반환
UserService 클래스 생성 및 구현
package com.tenco.bank.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.tenco.bank.dto.SignUpDTO;
import com.tenco.bank.handler.exception.DataDeliveryException;
import com.tenco.bank.handler.exception.RedirectException;
import com.tenco.bank.repository.interfaces.UserRepository;
@Service // IoC 대상(싱글톤으로 해당 객체를 관리)
public class UserService {
@Autowired
private final UserRepository userRepository;
// 생성자 의존 주입 DI
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
// 회원 가입 처리
// 예외 처리
// DB 에서 연결이나 쿼리 실행, 제약 사항 위한 같은
// 예외는 RuntimeException 으로 예외를 잡을 수 없습니다.
@Transactional // 트랜 잭션 처리 습관
public void createUser(SignUpDTO dto) {
// Http 응답으로 클라이언트에게 전달할 오류 메시지는 최소한으로 유지하고,
// 보안 및 사용자 경험 측면에서 민감한 정보를 노출하지 않도록 합니다.
int result = 0;
try {
result = userRepository.insert(dto.toUser());
// 여기서 예외 처리를 하면 상위 catch 블록에서 예외를 잡는다.
} catch (DataAccessException e) {
// DataAccessException는 Spring의 데이터 액세스 예외 클래스로,
// 데이터베이스 연결이나 쿼리 실행과 관련된 문제를 처리합니다.
throw new DataDeliveryException("잘못된 처리 입니다",HttpStatus.INTERNAL_SERVER_ERROR);
} catch (Exception e) {
// 그 외 예외 처리 - 페이지 이동 처리
throw new RedirectException("알 수 없는 오류" , HttpStatus.SERVICE_UNAVAILABLE);
}
// 예외 클래스가 발생이 안되지만 프로세스 입장에서 예외 상황으로 바라 봄
if (result != 1) {
// 삽입된 행의 수가 1이 아닌 경우 예외 발생
throw new DataDeliveryException("회원 가입 실패", HttpStatus.INTERNAL_SERVER_ERROR);
}
}
}
UserService 설계
UserService는 회원 가입 로직을 처리하는 서비스 계층입니다.
📌 의존성 주입
- UserRepository를 생성자 주입 방식으로 주입
📌 회원 가입 처리
- 트랜잭션 보장: @Transactional을 사용하여 모든 작업이 성공적으로 수행될 때만 DB에 반영
- 예외 처리:
- DataAccessException: DB 연결 및 쿼리 실행 중 발생하는 예외 처리
- Exception: 알 수 없는 오류 처리 후 리다이렉트 예외 발생
📌 트랜잭션 처리 (@Transactional)
- 회원 생성이 완료되지 않으면 자동으로 롤백
- DB 관련 예외(DataAccessException)가 발생하면 500 (INTERNAL_SERVER_ERROR) 반환
user.xml 파일 (MyBatis 쿼리 정의)
회원 정보를 user_tb 테이블에 삽입하는 SQL 문
<insert id="insert">
insert into user_tb (username, password, fullname, created_at)
values( #{username}, #{password}, #{fullname}, now())
</insert>
📌 #{}를 사용하여 MyBatis가 DTO 값을 동적으로 바인딩
테스트 시나리오
케이스입력 데이터예상 결과
✅ 정상 회원 가입 | username=hong, password=1234, fullname=홍길동 | 회원 가입 성공 및 로그인 페이지 리다이렉트 |
❌ username 누락 | password=1234, fullname=홍길동 | 400 BAD_REQUEST - username 입력 요청 |
❌ password 누락 | username=hong, fullname=홍길동 | 400 BAD_REQUEST - password 입력 요청 |
❌ fullname 누락 | username=hong, password=1234 | 400 BAD_REQUEST - fullname 입력 요청 |
📌 검증 도구:
- Postman을 사용하여 HTTP 요청을 보내고 응답을 확인
- Talend API Tester를 사용하여 API를 테스트
✅ 정리
1️⃣ 회원 가입 요청을 처리하는 DTO, Controller, Service 설계
2️⃣ 회원 정보 유효성 검사 및 예외 처리 (DataDeliveryException)
3️⃣ 트랜잭션 보장을 위한 @Transactional 사용
4️⃣ MyBatis user.xml을 활용한 회원 가입 SQL 작성
5️⃣ Postman 및 Talend API를 활용한 테스트 시나리오 검증
🚀 h2 스키마 및 초기 데이터 셋팅과 회원 가입 화면 구현 다음 포스팅에서!
'Spring' 카테고리의 다른 글
[Spring boot] Bank App - 로그인 처리 (0) | 2025.02.17 |
---|---|
Spring[Spring boot] Bank App - 회원가입(화면구현) (2) | 2025.02.15 |
[Spring boot] Bank App - MyBatis 설정 (DB 접근 기술) (0) | 2025.02.14 |
[Spring boot] Bank App - Exception Handler (0) | 2025.02.13 |
[Spring boot] Bank App - MainController, mainPage.jsp 구현 (2) | 2025.01.15 |