BankApp - 회원가입(트랜잭션 처리)

Gun·2023년 9월 18일

Spring Boot - BankApp

목록 보기
11/25
💡 학습목표
   1. dto 설계 하기 및 개념 확인
   2. UserController 설계 및 유효성 검사

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

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

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

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

트랜잭션에 이해

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

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

   즉, 트랜잭션에서 한 번에 처리되어야 하는 작업들은 모두 완전히 수행되거나, 아예 수행되지 않아야 합니다.

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

   Spring Boot에서는 트랜잭션을 처리하기 위해 @Transactional 어노테이션을 제공합니다. 
   이 어노테이션을 사용하면, 트랜잭션 범위 내에서 실행되는 모든 작업이 원자성을 갖도록 보장할 수 있습니다. 

   또한, 트랜잭션의 고립성, 일관성, 지속성 등의 ACID 성질을 보장하기 위해 다양한 설정 옵션을 제공합니다.

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

💡 Web Layer : Rest API를 제공하며, Client 중심의 로직 적용
   Business Layer : 내부 정책에 따른 logic를 개발하며, 주로 핵심 업무 부분을 개발
   Data Layer : 데이터 베이스 및 외부와의 연동 처리
💡 서비스는 일반적으로 비즈니스 로직을 수행하는 클래스입니다.

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

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

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

UserController.java


package com.tencoding.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.tencoding.bank.dto.SignUpFormDto;
import com.tencoding.bank.handler.exception.CustomRestfulException;
import com.tencoding.bank.service.UserService;

@Controller
@RequestMapping("/user")
public class UserController {

	@Autowired
	private UserService userService;

	// 회원 가입 페이지 요청
	// http://localhost:80/user/sign-up

	@GetMapping("/sign-up")
	public String signUp() {
		return "user/signUp";
	}

	// 로그인 페이지 요청
	// http://localhost:80/user/sign-in
	@GetMapping("/sign-in")
	public String signIn() {
		return "user/signIn";
	}
	
	// 회원 가입 처리 
	// @param signUpFormDto
	// @return 리다이렉트 처리 - 로그인 페이지
	
	@PostMapping("/sign-up")
	public String signUpProc(SignUpFormDto signUpFormDto) {
		// 1. 유효성 검사
		if(signUpFormDto.getUsername() == null
				|| signUpFormDto.getUsername().isEmpty()) {
			throw new CustomRestfulException("username을 입력하세요", HttpStatus.BAD_REQUEST);
		}
		
		// 2. 유효성 검사 2
		if(signUpFormDto.getPassword() == null
				|| signUpFormDto.getPassword().isEmpty()) {
			throw new CustomRestfulException("password을 입력하세요", HttpStatus.BAD_REQUEST);
		}
		
		// 3. 유효성 검사 3
		if(signUpFormDto.getFullname() == null
				|| signUpFormDto.getFullname().isEmpty()) {
			throw new CustomRestfulException("fullname을 입력하세요", HttpStatus.BAD_REQUEST);
		}
		
		
		// 로직 추가 -- 서비스 호출
		userService.signUp(signUpFormDto);
		return "redirect:/user/sign-in";
	}
}

UserSevice.java


package com.tencoding.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.tencoding.bank.dto.SignUpFormDto;
import com.tencoding.bank.handler.exception.CustomRestfulException;
import com.tencoding.bank.repository.interfaces.UserRepository;

@Service // IoC 대상 - 싱글톤 패턴
public class UserService{

	// DAO - 데이터 베이스 연동
	@Autowired
	private UserRepository userRepository;
	
	// DI(Dependency Injection) - 가지고 오다
//	public UserService(UserRepository userRepository) {
//	this.userRepository = userRepository;
//	}
	
	// 트랜잭션 사용하는 이유는 정상 처리 commit(반영)
	// 정상 처리 되지 않으면 Rollback 처리 됨
	@Transactional
	public void signUp(SignUpFormDto signUpFormDto) {
		int result = userRepository.insert(signUpFormDto);
		System.out.println("result : " + result);
		if(result != 1) {
			throw new CustomRestfulException("회원가입 실패", HttpStatus.INTERNAL_SERVER_ERROR);
		}
	}
}

UserRepository.java


package com.tencoding.bank.repository.interfaces;

import java.util.List;

import org.apache.ibatis.annotations.Mapper;

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


// ibatis -> 2.4 버전 이후로 MyBatis로 이름 변경 됨
@Mapper // Mapper 반드시 기술을 해주어야 동작한다.
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();
	
}

user.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.tencoding.bank.repository.interfaces.UserRepository">
 
	<insert id="insert">
		insert into user_tb(username, password, fullname, created_at)
		values(#{username}, #{password}, #{fullname}, now())
	</insert>
	
	<update id="updateById">
		update user_tb set username = #{username},
		password = ${password},
		fullname = ${fullname}
		where id = ${id}
	</update>

	<delete id="deleteById">
		delete from user_tb where id = #{id} 
	</delete>
	
	<select id="findById" resultType="com.tencoding.bank.repository.model.User">
		select * from user_tb where id = ${id}
	</select>
	
	<select id="findAll" resultType="hashmap">
		select * from user_tb
	</select>
	
</mapper>

0개의 댓글