본문으로 바로가기
1. dto 설계 하기 및 개념 확인(사전 기반 지식)
2. UserController, UserService 설계 및 유효성 검사, 예외 처리
3. h2 스키마 및 초기 데이터 셋팅
4. 회원 가입 화면 구현

 

1. UserController, UserService 설계 및 유효성 검사, 예외 처리

SignUpDTO

package com.tenco.bank.dto;

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

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;

@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
@ToString
public class SignUpDTO {
	
	private String username; 
	private String password; 
	private String fullname;
	
	// 2단계 로직 - User Object 반환 
	public User toUser() {
		return User.builder()
				.username(this.username)
				.password(this.password)
				.fullname(this.fullname)
				.build();
	} 
	
	// todo - 추후 사진 업로드 기능 추가 예정 	
}

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 // IoC에 대상(싱글톤 패턴으로 관리됨) 
@RequestMapping("/user") // 대문 처리 
public class UserController {
	
	@Autowired // DI 처리 
	private UserService userService;

	/**
	 * 회원 가입 페이지 요청 
	 * 주소 설계 : http://localhost:8080/user/sign-up
	 * @return signUp.jsp 
	 */
	@GetMapping("/sign-up")
	public String signUpPage() {
		// prefix: /WEB-INF/view/
		// return: user/signUp
		// suffix: .jsp 
		return "user/signUp";
	}
	
	/**
	 * 회원 가입 로직 처리 요청
	 * 주소 설계 : http://localhost:8080/user/sign-up
	 * @param dto
	 * @return
	 */
	@PostMapping("/sign-up")
	public String signUpProc(SignUpDTO dto) {
		
		// controller 에서 일반적이 코드 작업 
		// 1. 인증검사 (여기서는 인증검사 불 필요) 
		// 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:/index";
	}
}

 

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 UserRepository userRepository;
	
//  @Autowired 어노테이션으로 대체 가능 하다.
//  생성자 의존 주입 - DI 	
//	public UserService(UserRepository userRepository) {
//		this.userRepository = userRepository;
//	}
	
	/**
	 * 회원 등록 서비스 기능
	 * 트랜잭션 처리  
	 * @param dto
	 */
	@Transactional // 트랜잭션 처리는 반드시 습관화 
	public void createUser(SignUpDTO dto) {
		int result = 0; 
		try {
			result = userRepository.insert(dto.toUser());
		} catch (DataAccessException e) {
			throw new DataDeliveryException("잘못된 처리입니다", HttpStatus.INTERNAL_SERVER_ERROR);
		} catch (Exception e) {
			throw new RedirectException("알 수 없는 오류", HttpStatus.SERVICE_UNAVAILABLE);
		}
		if(result != 1) {
			throw new DataDeliveryException("회원가입 실패", HttpStatus.INTERNAL_SERVER_ERROR);
		}
	}
}

 

user.xml 파일 수정

 

	<insert id="insert">
		insert into user_tb(username, password, fullname, created_at) 
		values( #{username}, #{password}, #{fullname}, now())
	</insert>

 

테스트 값 입력 및 검증하기 (탤런트 API, 포스트 맨 활용)

 

3. h2 스키마 및 초기 데이터 셋팅

 


💠 h2 로컬 DB 사용 중(개발시) 확인 사항

build.gradle 파일에 의존성 설정 확인 - runtimeOnly 'com.h2database:h2’

yml db 설정 확인

url: jdbc:h2:mem:bankdb;MODE=MySQL # 데이터베이스 연결을 위한 URL을 설정합니다. driver-class-name: org.h2.Driver # JDBC 드라이버 클래스를 설정합니다. username: sa # 데이터베이스 연결을 위한 사용자 이름을 설정합니다. password: '' # 데이터베이스 연결을 위한 비밀번호를 설정합니다. 여기서는 비밀번호를 빈 문자열로 설정했습니다.

h2 DB 접근 방법
로컬 서버 실행
브라우저에서 주소 입력 http://localhost:8080/h2-console


h2 스키마 및 초기 데이터 작업 순서

1. yml 파일에 서버가 매번 실행 될 때 마다 테이블을 생성하고 초기 데이터를 insert 할 수 있도록 설정
2. resourcese/db 패키지 생성 및 table.sql 파일 생성 data.sql 파일 생성
3. sql 쿼리문 입력 (복사 붙여넣기)
4. H2 인 메모리 접근 및 데이터 확인

 

 

table.sql

create table user_tb(
	id int auto_increment primary key, 
    username varchar(50) not null unique, 
	password varchar(100) not null, 
	fullname varchar(50) not null, 
    created_at timestamp not null default now()
); 

create table account_tb(
	id int auto_increment primary key, 
    number varchar(30) not null unique, 
    password varchar(30) not null, 
    balance bigint not null comment '계좌잔액', 
    created_at timestamp not null default now(), 
	user_id int 
);

create table history_tb(
	id int auto_increment primary key comment '거래내역 ID',
	amount bigint not null comment '거래금액',
    w_account_id int comment '출금 계좌 ID',
    d_account_id int comment '입금 계좌 ID',
    w_balance bigint comment '출금 요청 후 계좌 잔액',
    d_balance bigint comment '입금 요청 후 계좌 잔액', 
    created_at timestamp not null default now() 
);

 

data.sql

insert into user_tb(username, password, fullname, created_at)
values('길동', '1234', '고', now());

insert into user_tb(username, password, fullname, created_at)
values('둘리', '1234', '애기공룡', now());

insert into user_tb(username, password, fullname, created_at)
values('마이', '1234', '콜', now());


insert into account_tb
		(number, password, balance, user_id, created_at)
values('1111', '1234', 1300, 1, now());        

insert into account_tb
		(number, password, balance, user_id, created_at)
values('2222', '1234', 1100, 2, now());        

insert into account_tb
		(number, password, balance, user_id, created_at)
values('3333', '1234', 0, 3, now());


insert into history_tb(amount, w_balance, d_balance, w_account_id, d_account_id, created_at)
			values(100, 900, 1100, 1, 2, now());

-- 2. ATM 기기에서 출금 
-- 1111 계좌에서 100원만 출금하는 히스토리를 만들어 보세요 
insert into history_tb(amount, w_balance, d_balance, w_account_id, d_account_id, created_at)
			values(100, 800, null, 1, null, now());

-- 3. ATM 기기에서 입금 
-- 1111 계좌로 500원만 입금하는 히스토리를 만들어 보세요 
insert into history_tb(amount, w_balance, d_balance, w_account_id, d_account_id, created_at)
			values(500, null, 1300, null, 1, now());

 

 

(mysql설정 - 의존성 충돌시 로컬 DB로 사용하세요)

spring:
  mvc:
    view:
      prefix: /WEB-INF/view/
      suffix: .jsp
  servlet:
    multipart:
      max-file-size: 20MB #파일 용량 설정 최대 20MB   
      max-request-size: 20MB
  datasource:
    url: jdbc:mysql://localhost:3306/mybank?serverTimeZone=Asia/Seoul
    driver-class-name: com.mysql.cj.jdbc.Driver
    username: root
    password: asd1234

 

yml 파일 확인

server:
  port: 8080 #서버가 사용할 포트 번호 설정 
  servlet:
    encoding:
      charset: utf-8 #서블릿의 응답과 요청 인코딩을 UTF-8 로 설정 
      force: true # 요청과 응답에 대해 이 인코딩을 강제로 사용하도록 설정합니다. 
      
spring:
  mvc:
    view: 
      prefix: /WEB-INF/view/ #JSP파일이 위치한 디렉토리 접두사를 설정합니다.
      suffix: .jsp #뷰 이름에 자동으로 추가될 파일 확장자를 설정합니다.
  datasource:
    url: jdbc:h2:mem:bankdb;MODE=MySQL  #데이터 베이스 연결을 위한 URL을 설정 합니다.
    driver-class-name: org.h2.Driver #드라이버 클래스를 설정 합니다.
    username: sa #사용자 ID를 지정
    password: '' #DB 비밀번호 여기서는 빈 문자열로 설정
  sql:
    init:
      schema-locations:
      - classpath:db/table.sql
      data-locations:
      - classpath:db/data.sql 
  
  h2:
    console:
      enabled: true #H2 데이터 베이스 콘솔을 활성화 합니다.   
  
  output:
    ansi:
      enabled: always #콘솔 출력에 ANSI 색상 코드를 사용할 수 있도록 설정 
      
#mybatis 설정
mybatis:
  mapper-locations:
    - classpath:mapper/**/*.xml  #MyBatis 매퍼 파일 위치를 설정합니다. **은 모든 디렉토리, *.xml 은 모든 XML 파일을 의미합니다.
  configuration:
    map-underscore-to-camel-case: true #데이터베이스의 언더스코어 네이밍(column_name)을 카멜 케이스(columnName)로 자동 매핑합니다.
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #SQL 로깅 구현체를 설정합니다.        

logging:
  level:
    org.apache.ibatis: DEBUG #MyBatis 로깅 레벨을 DEBUG로 설정하여 실행되는 SQL 쿼리와 내부 로깅 정보를 콘솔에 출력합니다.

 

 

@Autowired 오류 때문에 코드 수정

 

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 // IoC에 대상(싱글톤 패턴으로 관리됨) 
@RequestMapping("/user") // 대문 처리 
public class UserController {
	
	 
	private UserService userService;
	
	// DI 처리 
	@Autowired // 노란색 경고는 사용할 필요 없음 - 가독성 위해서 선언해도 됨
	public UserController(UserService service) {
		this.userService = service;
	}
	
	/**
	 * 회원 가입 페이지 요청 
	 * 주소 설계 : http://localhost:8080/user/sign-up
	 * @return signUp.jsp 
	 */
	@GetMapping("/sign-up")
	public String signUpPage() {
		// prefix: /WEB-INF/view/
		// return: user/signUp
		// suffix: .jsp 
		return "user/signUp";
	}
	
	/**
	 * 회원 가입 로직 처리 요청
	 * 주소 설계 : http://localhost:8080/user/sign-up
	 * @param dto
	 * @return
	 */
	@PostMapping("/sign-up")
	public String signUpProc(SignUpDTO dto) {
		
		// controller 에서 일반적이 코드 작업 
		// 1. 인증검사 (여기서는 인증검사 불 필요) 
		// 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:/index";
	}
}

 

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 {
	
	
	private UserRepository userRepository;
	
//  @Autowired 어노테이션으로 대체 가능 하다.
//  생성자 의존 주입 - DI 	
	@Autowired
	public UserService(UserRepository userRepository) {
		this.userRepository = userRepository;
	}
	
	/**
	 * 회원 등록 서비스 기능
	 * 트랜잭션 처리  
	 * @param dto
	 */
	@Transactional // 트랜잭션 처리는 반드시 습관화 
	public void createUser(SignUpDTO dto) {
		int result = 0; 
		try {
			result = userRepository.insert(dto.toUser());
		} catch (DataAccessException e) {
			throw new DataDeliveryException("잘못된 처리입니다", HttpStatus.INTERNAL_SERVER_ERROR);
		} catch (Exception e) {
			throw new RedirectException("알 수 없는 오류", HttpStatus.SERVICE_UNAVAILABLE);
		}
		if(result != 1) {
			throw new DataDeliveryException("회원가입 실패", HttpStatus.INTERNAL_SERVER_ERROR);
		}
	}	
}

 

 

4. 회원 가입 화면 구현

 

 

부트스트랩 4 Form 태그 활용

https://www.w3schools.com/bootstrap4/bootstrap_forms.asp

 

W3Schools.com

W3Schools offers free online tutorials, references and exercises in all the major languages of the web. Covering popular subjects like HTML, CSS, JavaScript, Python, SQL, Java, and many, many more.

www.w3schools.com

 

signUp.jsp 파일을 생성해주세요

 

 

signUp.jsp 작업 순서
1. mainPage.jsp 파일에서 코드를 복사후에 signUp.jsp 파일로 붙여 넣기 합니다.
2. Bootstrap 4 Form 태그에서 코드를 복사해서 가져 옵니다.

 

 

user/signUp.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

<!-- header.jsp  -->
<%@ include file="/WEB-INF/view/layout/header.jsp"%>

<!-- start of content.jsp(xxx.jsp)   -->
<div class="col-sm-8">
	<h2>회원 가입</h2>
	<h5>Bank App에 오신걸 환영합니다</h5>
	
	<form action="/">
		<div class="form-group">
			<label for="username">username:</label>
			<input type="text" class="form-control" placeholder="Enter username" id="username" name="username">
		</div>
		<div class="form-group">
			<label for="pwd">Password:</label>
			<input type="password" class="form-control" placeholder="Enter password" id="pwd" name="password">
		</div>
		<div class="form-group">
			<label for="fullname">fullname:</label>
			<input type="text" class="form-control" placeholder="Enter fullname" id="fullname" name="fullname">
		</div>
		<button type="submit" class="btn btn-primary">회원가입</button>
	</form>

</div>
<!-- end of col-sm-8  -->
</div>
</div>
<!-- end of content.jsp(xxx.jsp)   -->

<!-- footer.jsp  -->
<%@ include file="/WEB-INF/view/layout/footer.jsp"%>

 

 

  • 도전 과제로 - 스프링 부트 기본 파싱전략이 어떻게 되는지 조사해보세요
  • 반드시 name 속성을 넣어 주어야 합니다.
  • 코드 테스트 및 개발 단계에 기본값을 넣어 둘 수 있도록 습관을 가져 보세요

 

 

회원 가입 결과 확인 및 오류 테스트

 

 

 

username 컬럼에는 DB 제약 사항인 유니크 키가 걸려 있습니다.

 

'Java' 카테고리의 다른 글

콜백 메서드 구현  (0) 2024.09.25
람다식(Lambda expression)  (2) 2024.09.13
# 8. 화면 구현 - 2(레이아웃 분리)  (0) 2024.08.06
Wrapper 클래스  (0) 2024.06.12
Swing - (이미지 겹치는 방법)  (0) 2024.04.29