

컨트롤러가 직접 DB에 접근하게 되면, 메소드가 중복된다.
중복을 제거하려면?
공통 부분을 따로 분리해낸다.
계층의 분리!

UserDao를 통해 DB에 접근한다면
중복코드가 제거된다.
이 UserDao를 영속 계층(Persistence Layer), Data Access Layer 라고 하고,
컨트롤러 부분은 Presentation Layer라고 한다. 데이터를 보여주는 계층이다.
이렇게 분리해내는 이유는
1.관심사의 분리 / 2. 변하는 것과 변하지 않는 것 / 3. 중복, 공통 코드
1,3번이다.
나중에 MySQL을 쓰던지 Oracle을 쓰던지, 인터페이스를 사용하면 UserDao가 바뀌어도 상관없다.
UserDao.java를 만들고,
UserDaoImpl을 만들었다.
UserDao를 인터페이스로 만들고 구현체로 UserDaoImpl을 빼냈다.
<code />
package com.fastcampus.ch3;
import org.springframework.beans.factory.annotation.Autowired;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Date;
public class UserDaoImpl implements UserDao {
@Autowired
DataSource ds;
final int FAIL = 0;
@Override
public int deleteUser(String id) {
int rowCnt = FAIL; // insert, delete, update
Connection conn = null;
PreparedStatement pstmt = null;
String sql = "delete from user_info where id= ? ";
try {
conn = ds.getConnection();
pstmt = conn.prepareStatement(sql);
pstmt.setString(1, id);
// int rowCnt = pstmt.executeUpdate(); // insert, delete, update
// return rowCnt;
return pstmt.executeUpdate(); // insert, delete, update
} catch (SQLException e) {
e.printStackTrace();
return FAIL;
} finally {
// close()를 호출하다가 예외가 발생할 수 있으므로, try-catch로 감싸야함.
// try { if(pstmt!=null) pstmt.close(); } catch (SQLException e) { e.printStackTrace();}
// try { if(conn!=null) conn.close(); } catch (SQLException e) { e.printStackTrace();}
close(pstmt, conn); // private void close(AutoCloseable... acs) {
}
}
@Override
public User selectUser(String id) {
User user = null;
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
String sql = "select * from user_info where id= ? ";
try {
conn = ds.getConnection();
pstmt = conn.prepareStatement(sql); // SQL Injection공격, 성능향상
pstmt.setString(1, id);
rs = pstmt.executeQuery(); // select
if (rs.next()) {
user = new User();
user.setId(rs.getString(1));
user.setPwd(rs.getString(2));
user.setName(rs.getString(3));
user.setEmail(rs.getString(4));
user.setBirth(new Date(rs.getDate(5).getTime()));
user.setSns(rs.getString(6));
user.setRed_date(new Date(rs.getTimestamp(7).getTime()));
}
} catch (SQLException e) {
return null;
} finally {
// close()를 호출하다가 예외가 발생할 수 있으므로, try-catch로 감싸야함.
// close()의 호출순서는 생성된 순서의 역순
// try { if(rs!=null) rs.close(); } catch (SQLException e) { e.printStackTrace();}
// try { if(pstmt!=null) pstmt.close(); } catch (SQLException e) { e.printStackTrace();}
// try { if(conn!=null) conn.close(); } catch (SQLException e) { e.printStackTrace();}
close(rs, pstmt, conn); // private void close(AutoCloseable... acs) {
}
return user;
}
// 사용자 정보를 user_info테이블에 저장하는 메서드
@Override
public int insertUser(User user) {
int rowCnt = FAIL;
Connection conn = null;
PreparedStatement pstmt = null;
// insert into user_info (id, pwd, name, email, birth, sns, reg_date)
// values ('asdf22', '1234', 'smith', 'aaa@aaa.com', '2022-01-01', 'facebook', now());
String sql = "insert into user_info values (?, ?, ?, ?,?,?, now()) ";
try {
conn = ds.getConnection();
pstmt = conn.prepareStatement(sql); // SQL Injection공격, 성능향상
pstmt.setString(1, user.getId());
pstmt.setString(2, user.getPwd());
pstmt.setString(3, user.getName());
pstmt.setString(4, user.getEmail());
pstmt.setDate(5, new java.sql.Date(user.getBirth().getTime()));
pstmt.setString(6, user.getSns());
return pstmt.executeUpdate(); // insert, delete, update;
} catch (SQLException e) {
e.printStackTrace();
return FAIL;
} finally {
close(pstmt, conn); // private void close(AutoCloseable... acs) {
}
}
// 매개변수로 받은 사용자 정보로 user_info테이블을 update하는 메서드
@Override
public int updateUser(User user) {
int rowCnt = FAIL; // insert, delete, update
// Connection conn = null;
// PreparedStatement pstmt = null;
String sql = "update user_info " +
"set pwd = ?, name=?, email=?, birth =?, sns=?, reg_date=? " +
"where id = ? ";
// try-with-resources - since jdk7
try (
Connection conn = ds.getConnection();
PreparedStatement pstmt = conn.prepareStatement(sql); // SQL Injection공격, 성능향상
){
pstmt.setString(1, user.getPwd());
pstmt.setString(2, user.getName());
pstmt.setString(3, user.getEmail());
pstmt.setDate(4, new java.sql.Date(user.getBirth().getTime()));
pstmt.setString(5, user.getSns());
pstmt.setTimestamp(6, new java.sql.Timestamp(user.getRed_date().getTime()));
pstmt.setString(7, user.getId());
rowCnt = pstmt.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
return FAIL;
}
return rowCnt;
}
@Override
public void deleteAll() throws Exception {
Connection conn = ds.getConnection();
String sql = "delete from user_info ";
PreparedStatement pstmt = conn.prepareStatement(sql); // SQL Injection공격, 성능향상
pstmt.executeUpdate(); // insert, delete, update
}
private void close(AutoCloseable... acs) {
for(AutoCloseable ac :acs)
try { if(ac!=null) ac.close(); } catch(Exception e) { e.printStackTrace(); }
}
}
그리고 빼낸 후
TDD 테스트를 만들었다.
<code />
package com.fastcampus.ch3;
import junit.framework.TestCase;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.util.Calendar;
import java.util.Date;
@RunWith(SpringJUnit4ClassRunner.class) //ac를 자동으로 만들어줌
@ContextConfiguration(locations = {"file:src/main/webapp/WEB-INF/spring/**/root-context.xml"}) //xml 경로 지정해주기
public class UserDaoImplTest extends TestCase {
@Autowired
UserDao userDao;
@Test
public void testDeleteUser() {
}
@Test
public void testSelectUser() {
}
@Test
public void testInsertUser() {
}
@Test
public void testUpdateUser() throws Exception{
Calendar cal = Calendar.getInstance();
cal.clear(); //모든 시간 필드 초기화
cal.set(2023,3,29); //날짜만 넣기
userDao.deleteAll();
User user = new User("asdf","1111","abc","aaa@aaa.com",new Date(cal.getTimeInMillis()), "fb",new Date());
int rowCnt=userDao.insertUser(user);
assertTrue(rowCnt==1);
user.setPwd("4321");
user.setEmail("bbb@bbb.com");
rowCnt=userDao.updateUser(user);
assertTrue(rowCnt==1);
User user2 = userDao.selectUser(user.getId());
System.out.println("user = " + user);
System.out.println("user2 = " + user2);
assertTrue(user.equals(user2));
}
}
RegisterController.java
LoginController.java
UserValidator.java
index.jsp
loginForm.jsp
registerForm.jsp
registerInfo.jsp
파일들 추가
1. LoginController에 UserDao를 주입하고 메서드 수정
<code />
private boolean loginCheck(String id, String pwd) {
User user = userDao.selectUser(id);
if(user==null) return false;
return user.getPwd().equals(pwd);
// return "asdf".equals(id) && "1234".equals(pwd);
}
2. RegisterContoller에 UserDao를 주입하고 메서드에 내용 채워줌
<code />
@PostMapping("/add")
public String save(@Valid User user, BindingResult result, Model m) throws Exception {
System.out.println("result="+result);
System.out.println("user="+user);
// User객체를 검증한 결과 에러가 있으면, registerForm을 이용해서 에러를 보여줘야 함.
if(!result.hasErrors()) {
// 2. DB에 신규회원 정보를 저장
int rowCnt = userDao.insertUser(user);
if(rowCnt!=FAIL) {
return "registerInfo";
}
}
return "registerForm";
}
3. servlet-context.xml에
redirect:/가
index.html이 될 수 있도록 설정
<code />
<view-controller path="/" view-name="index"/>
resources도 바꿔줌
<code />
<!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources directory -->
<resources mapping="/**" location="/resources/" />
에러 메시지 파일을 읽을 수 있도록
아래 코드도 넣어줌
<java />
<beans:bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
<beans:property name="basenames">
<beans:list>
<beans:value>error_message</beans:value> <!-- /src/main/resources/error_message.properties -->
</beans:list>
</beans:property>
<beans:property name="defaultEncoding" value="UTF-8"/>
</beans:bean>
4.DB에 넣은 한글이 깨져서
web.xml에 아래 코드 추가
<java />
<!-- 한글 변환 필터 시작 -->
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 한글 변환 필터 끝 -->

5.error_message.properties 파일 추가
<java />
required=필수 항목입니다.
required.user.pwd=사용자 비밀번호는 필수 항목입니다.
invalidLength.user.id=아이디의 길이는 {0}~{1}사이어야 합니다.
'패캠 챌린지' 카테고리의 다른 글
AOP (0) | 2023.04.04 |
---|---|
Transaction, Commit, Rollback (0) | 2023.04.03 |
스프링으로 DB 다루기 (0) | 2023.03.29 |
Spring으로 DB 연결하기 (0) | 2023.03.28 |
스프링 IOC와 DI (0) | 2023.03.24 |