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을 빼냈다.

 

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 테스트를 만들었다.

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를 주입하고 메서드 수정

    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를 주입하고 메서드에 내용 채워줌

@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이 될 수 있도록 설정

<view-controller path="/" view-name="index"/>

 

resources도 바꿔줌

<!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources directory -->
<resources mapping="/**" location="/resources/" />

 

에러 메시지 파일을 읽을 수 있도록 

아래 코드도 넣어줌

	<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에 아래 코드 추가

<!-- 한글 변환 필터 시작 -->
	<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 파일 추가

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

@개발자 김혜린

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