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
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 |