1. dto 설계 하기 및 개념 확인
2. UserController 설계 및 유효성 검사
3. UserService 설계와 DI 사용 
4. service layer에 이해 
5. 트랜잭션에 이해 (insert, update, delete 트랜잭션 처리 , 복잡한 select 트랜잭션 처리)

DTO 패키지와 Model 패키지를 분리하는 것이 바람직하다.

DTO(Data Transfer Object)와 모델 클래스를 분리하여 패키지를 만드는 것이 좋습니다. 
그 이유는 다음과 같습니다:

1. **코드의 가독성 및 유지 보수성**: DTO와 모델 클래스를 별도의 패키지로 구분함으로써 
코드의 구조가 명확해지고, 관련 클래스를 찾기 쉬워집니다. 이를 통해 유지 보수성이 향상됩니다.

2. **객체의 역할 구분**: 모델 클래스는 데이터베이스의 테이블 구조를 표현하는 반면, 
DTO는 클라이언트와 서버 간의 데이터 전송을 담당합니다. 이 두 객체의 역할이 다르기 때문에, 
별도의 패키지로 구분하는 것이 좋습니다.
3. **유연한 변경**: 애플리케이션의 요구 사항이 변경되면 DTO와 모델 클래스의 변경이 독립적으로 이루어질 수 있습니다. 
이렇게 구조를 분리해 놓으면, 한쪽의 변경이 다른 쪽에 영향을 미치는 것을 최소화할 수 있습니다.

따라서, 코드의 가독성, 유지 보수성 및 유연성을 높이기 위해 DTO 패키지와 모델 패키지를 따로 구성하는 것이 좋습니다.

서비스레이어를 만드는게 좋습니다.(만드는 이유)

코드 결합도를 낮추고 재 사용성을 높이기 위함 ( 트랜잭션 처리 ) - 서비스를 사용하는 이유

Web Layer : Rest API를 제공하며, Client 중심의 로직 적용
Business Layer : 내부 정책에 따른 logic를 개발하며, 주로 핵심업무 부분을 개발
Data Layer : 데이터 베이스 및 외부와의 연동 처리

**서비스는 일반적으로 비즈니스 로직을 수행하는 클래스입니다.**

비즈니스 로직(Business Logic)이란, 애플리케이션에서 수행되는 비즈니스 규칙이나 
프로세스 등을 의미합니다. 즉, 애플리케이션의 핵심적인 업무 처리를 담당하는 로직을 말합니다

예를 들어, 온라인 쇼핑몰에서 주문을 처리하는 기능은 주문 상태를 변경하거나, 
재고 수량을 업데이트하는 등의 처리 과정을 수행합니다. 이러한 과정들이 바로 비즈니스 로직에 해당됩니다.
또한, 비즈니스 로직은 애플리케이션에서 수행되는 모든 로직 중에서 가장 중요한 부분으로, 
애플리케이션의 성격과 특성에 따라 다양한 형태로 구현될 수 있습니다.

Spring Boot에서 비즈니스 로직은 주로 서비스(Service) 레이어에서 구현됩니다. 
이를 통해 비즈니스 로직을 분리하여 컨트롤러(Controller)나 뷰(View)와 같은 다른 레이어와 분리하여 개발하고,
애플리케이션의 유지 보수성과 확장성을 높일 수 있고 트랜잭션 관리도 가능 합니다.

트랜잭션에 이해

트랜잭션(Transaction)은 데이터베이스(Database)에서 수행되는 작업의 단위를 의미합니다. 
즉, 데이터베이스에서 데이터를 읽거나 쓰는 작업을 수행할 때, 한 번에 실행되어야 하는 일련의 작업을 의미합니다.


트랜잭션은 ACID라는 성질을 갖습니다. ACID는 원자성(Atomicity), 일관성(Consistency), 
고립성(Isolation), 지속성(Durability)의 약어입니다. 
이 중에서 원자성은 트랜잭션이 성공하거나 실패할 때, 모든 작업이 반영되거나, 
아무 것도 반영되지 않아야 한다는 것을 의미합니다. 
즉, 트랜잭션에서 한 번에 처리되어야 하는 작업들은 모두 완전히 수행되거나, 
아예 수행되지 않아야 합니다.

예를 들어, 은행에서 계좌 이체를 처리하는 작업을 수행할 때, 계좌에서 출금하는 작업과 입금하는 작업은
원자성을 가져야 합니다. 즉, 출금 작업이 완전히 수행되지 않았을 경우, 입금 작업도 반영되지 않아야 합니다.

Spring Boot에서는 트랜잭션을 처리하기 위해 @Transactional 어노테이션을 제공합니다.
이 어노테이션을 사용하면, 트랜잭션 범위 내에서 실행되는 모든 작업이 원자성을 갖도록 보장할 수 있습니다. 
또한, 트랜잭션의 고립성, 일관성, 지속성 등의 ACID 성질을 보장하기 위해 다양한 설정 옵션을 제공합니다.

UserController

/**
	 * 회원 가입 처리 
	 * @param signUpFormDto
	 * @return 리다이렉트 로그인 페이지
	 */
@PostMapping("/sign-up")
public String signUpProc(SignUpFormDto signUpFormDto) {
	
	// 1. 유효성 검사  
	if(signUpFormDto.getUsername() == null 
			|| signUpFormDto.getUsername().isEmpty()) {
		throw new CustomRestfullException("username을 입력해주세요", HttpStatus.BAD_REQUEST);
	}
	if(signUpFormDto.getPassword() == null 
			|| signUpFormDto.getPassword().isEmpty()) {
		throw new CustomRestfullException("password을 입력해주세요", HttpStatus.BAD_REQUEST);
	}
	if(signUpFormDto.getFullname() == null 
			|| signUpFormDto.getFullname().isEmpty()) {
		throw new CustomRestfullException("fullname을 입력해주세요", HttpStatus.BAD_REQUEST);
	} 
	// 서비스 호출 
	userService.signUp(signUpFormDto);
	
	return "redirect:/user/sign-in";
}
package com.tenco.bank.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.tenco.bank.dto.SignUpFormDto;
import com.tenco.bank.handler.exception.CustomRestfullException;
import com.tenco.bank.repository.interfaces.UserRepository;

@Service // IoC 대상 
public class UserService {
	
	@Autowired //DI 처리 (객체 생성시 의존 주의 처리)
	private UserRepository userRepository;
	
	@Transactional 
	// 메서드 호출이 시작될 때 트랜잭션에 시작
	// 메스드 종료시 트랜잭션 종료 (commit) 
	public void signUp(SignUpFormDto signUpFormDto) {
		// SignUpFormDto 
		// User 
		int result = userRepository.insert(signUpFormDto);
		if(result != 1) {
			throw new CustomRestfullException("회원 가입 실패", HttpStatus.INTERNAL_SERVER_ERROR);
		}
	}
}

코드 수정 1차 - (추후 수정 예정)

import java.util.List;

import org.apache.ibatis.annotations.Mapper;

import com.tenco.bank.dto.SignUpFormDto;
import com.tenco.bank.repository.model.User;

@Mapper  // MyBatis 의존 설정 함(build.gradle 파일) 
public interface UserRepository {
	// 코드 수정하기 
	public int insert(SignUpFormDto signUpFormDto);
	public int updateById(User user); 
	public int deleteById(Integer id);
	public User findById(Integer id); 
	public List<User> findAll();
}

트랜잭션 1차 연습

START TRANSACTION;

insert into user_tb(username, password, fullname)
values ('john', '1234', 'kim');

insert into user_tb(username, password, fullname)
values ('Mike', '222', 'Lee');

ROLLBACK;

트랜잭션 2차 연습

/*
	계좌 간 이체 
    계좌 A의 잔액은 3,000원 입니다. 
    계좌 B의 잔액은 0원 입니다. 
    계좌 A에서 B로 3000원 이체 하기 
*/
-- 테스트를 위한 설정 
UPDATE account_tb SET balance = 3000 where id = 1; 
UPDATE account_tb SET balance = 0 where id = 2; 

select * from account_tb;
select * from history_tb; 

START TRANSACTION;
UPDATE account_tb set balance = balance - 3000 WHERE id = 1; 
UPDATE account_tb set balance = balance + 3000 WHERE id = 2; 

INSERT INTO history_tb(amount, w_balance, 
	d_balance, w_account_id, d_account_id)
VALUES(3000, 
	(SELECT balance from account_tb where id = 1),
    (SELECT balance from account_tb where id = 2),
    1, 2);    

COMMIT;

 

'Spring boot > spring boot 앱 만들어 보기 2 단원' 카테고리의 다른 글

bank app - 계좌 생성하기  (0) 2023.04.19
bank app - 로그인 처리  (0) 2023.04.19
bank app - Exception Handler  (0) 2023.04.18
bank app - 화면 구현(2)  (0) 2023.04.17
bank app 화면 구현(1)  (0) 2023.04.17

+ Recent posts