Burninghering's Blog
article thumbnail
Published 2023. 3. 30. 00:01
DAO의 작성과 적용 패캠 챌린지

컨트롤러가 직접 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
profile

Burninghering's Blog

@개발자 김혜린

안녕하세요! 반갑습니다.