[LIKELION] 221020

고관운·2022년 10월 20일

회고

😄 느낀점

  • Spring을 이용하거나 객체 지향 프로그래밍을 통해 불필요한 중복을 방지할 수 있다고 생각했다.
  • 예외처리의 중요성!

😁 목표

  • 프로그래머스 : 두 정수 사이의 합, 수박수박수박수?
  • deleteAll(), .getCount()예외처리까지 복습
  • 모던 자바 인 액션 - 49 ~ 55까지 읽어오기

[멋쟁이사자처럼] 5주차 - Spring 입문

알고리즘(Stack)

Stack 추가 내용

기존 코드 변경 사항
arr의 타입을 int[] ➡ Integer[]로 변경
👉 다형성, int[]면 배열의 초기값 0, Integer[]이면 null

isEmpty()
Stack이 비어있다면 true, 아니라면 false 리턴

public boolean isEmpty() {
    return this.top == 0 ? true : false;
}

🔴 pop()이 비어있을 때 pop을 하면 오류 발생
👉 isEmpty()로 해결가능 (Stack이 비어있을 때 throw new EmptyStackException으로 처리)

peek()
Stack의 맨 위의 값을 가져옴 (top - 1의 값)
👉 peek 역시 Stack이 비어있을 때 EmptyStackException으로 처리

Test 파일이 있을 때 기존 Class의 명을 바꿀 경우

cannot find symbol class 오류 발생
👉 build의 rebuild Project 실행

Test

  • 각 Test는 독립적으로 실행되어야함
  • Test만들 때 원칙
  1. negative Test부터(안될 것 같은 것부터 테스트)
  2. 언제 실행해도 동일한 결과가 나오게 테스트 구성

실습

  • Create Test의 SetUp/@Before 체크
    👉 @BeforeEach : 각 테스트가 실행되기 전에 실행되는 구간 생성됨
  • assertEquals : 실행 결과와 비교하는 코드(Assertion을 앞에 안써줘도 됨)
  • Test 파일에서 Method 만들기(클래스에 없는 메소드라면 기존 클래스에 메소드 생성시킬 수 있음)

Modern Java 표현
🟢 왼쪽 코드를 오른쪽으로 사용

JUnit 작동방식

  1. 테스트 클래스에서 @Test가 붙은 public이고 void형이며 파라미터가 없는 테스트 메소드를 모두 찾는다.
  2. 테스트 클래스의 오브젝트를 하나 만든다.
  3. @Before가 붙은 메소드가 있으면 실행한다.
  4. @Test가 붙은 메소드를 하나 호출하고 테스트 결과를 저장해둔다.
  5. @After가 붙은 메소드가 있으면 실행한다. (의미는 try catch에서 finally의미)
  6. 나머지 테스트 메소드에 대해 2~5번을 반복한다.(테스트마다 Before 실행)
  7. 모든 테스트의 결과를 종합해서 돌려준다.

🟢 왜 2번에서 계속 오브젝트를 만들까? 👉 각 테스트가 독립적으로 실행됨을 보장해주기 위해

Spring

Spring 복습

과정

  1. UserDao복붙
  2. UserDaoTest만들기
  3. ConnectionMaker Interface만들기
  4. AwsConnectionMaker를 ConnectionMaker Interface 구현해서 만들기
  5. UserDao에 ConnectionMaker interface의존하게 변경
  6. UserDao에 Constructor 2개 추가
  7. UserDaoFactory만들기
  8. UserDaoFactory에 awsUserDao() 메소드 만들기
  9. build.gradle에 mysql, spring-boot-starter-jdbc, spring-boot-starter-test Dependency추가 하기
  10. UserDaoTest에 ExtendsWith, ContextConfiguration추가하기
  11. addAndGet() @Test 메소드 추가하기
  12. @Autowired ApplicationContext추가하기
  13. 테스트 메소드 구현
  14. 통과하는지 확인

코드 개선 과정

deleteAll(), getCount()추가 및 테스트
🟢 deleteAll()

public void deleteAll() throws SQLException, ClassNotFoundException {
        Connection conn = connectionMaker.getConnection();
        PreparedStatement ps = conn.prepareStatement("DELETE FROM users");
        
        ps.executeUpdate();
        
        ps.close();
        conn.close();
}

🟢 getCount()

public int getCount() throws SQLException, ClassNotFoundException {
        Connection conn = connectionMaker.getConnection();
        PreparedStatement ps = conn.prepareStatement("SELECT count(*) FROM users");
        
        ResultSet rs = ps.executeQuery();
        rs.next();
        int count = rs.getInt(1);
        
        rs.close();
        ps.close();
        conn.close();
        
        return count;
}

🟢 테스트 코드
1. userDao의 DB에 있는 users 테이블 데이터 삭제(deleteAll)
2. 이후 table의 개수를 0과 비교
3. 데이터를 하나씩 추가하며 비교하여 테스트

@Test
    void count() throws SQLException, ClassNotFoundException {
    	UserDao userDao = context.getBean("awsUserDao", UserDao.class);
        User user1 = new User("1", "박성철", "1234");
        User user2 = new User("2", "이길원", "2345");
        User user3 = new User("3", "박범진", "3456");
    
        userDao.deleteAll();
        assertEquals(0, userDao.getCount());

        userDao.add(user1);
        assertEquals(1, userDao.getCount());

        userDao.add(user2);
        assertEquals(2, userDao.getCount());

        userDao.add(user3);
        assertEquals(3, userDao.getCount());
}

addAndGet 개선
🟢 User 객체를 생성하여 findbyId 및 asserEquals할때 getter함수를 통해 값을 가져오도록 개선

@Test
    void addAndGet() throws SQLException, ClassNotFoundException {
    	UserDao userDao = context.getBean("awsUserDao", UserDao.class);
        User user1 = new User("1", "박성철", "1234");

        userDao.add(user1);
        assertEquals(1, userDao.getCount());

        User selectedUser = userDao.findbyId(user1.getId());
        assertEquals(user1.getName(), selectedUser.getName());
        assertEquals(user1.getPassword(), selectedUser.getPassword());
}

findbyId 예외처리
🟢 findbyId로 없는 Id를 찾을 경우 예외 발생
👉 rs가 있을 경우에만 값을 가져오고, 없을 경우에는 throw로 예외 처리

    public User findbyId(String id) throws SQLException, ClassNotFoundException {
        Connection conn = connectionMaker.getConnection();
        PreparedStatement ps = conn.prepareStatement("SELECT * from users where id = ?");
        ps.setString(1, id);
        ResultSet rs = ps.executeQuery();

        User user = null;
        if(rs.next()){
            user = new User(rs.getString("id"), rs.getString("name"), rs.getString("password"));
        }
        rs.close();
        ps.close();
        conn.close();

        if (user == null) throw new EmptyResultDataAccessException(1);

        return user;
    }

🔴 테스트코드 개선(@BeforeEach, @Autowired 활용)
1. @Autowired에 변수 선언
2. @BeforeEach에는 모든 Test가 중복되는 Connection과 변수 초기화

@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = UserDaoFactory.class)
class UserDaoTest {

    @Autowired
    private ApplicationContext context;
    private UserDao userDao;
    private User user1;
    private User user2;
    private User user3;

    @BeforeEach
    void setUp() {
        userDao = context.getBean("awsUserDao", UserDao.class);
        user1 = new User("1", "박성철", "1234");
        user2 = new User("2", "이길원", "2345");
        user3 = new User("3", "박범진", "3456");
    }

    @Test
    void addAndGet() throws SQLException, ClassNotFoundException {
        userDao.deleteAll();
        assertEquals(0, userDao.getCount());

        userDao.add(user1);
        assertEquals(1, userDao.getCount());

        User selectedUser = userDao.findbyId(user1.getId());
        assertEquals(user1.getName(), selectedUser.getName());
        assertEquals(user1.getPassword(), selectedUser.getPassword());
    }

    @Test
    void count() throws SQLException, ClassNotFoundException {
        userDao.deleteAll();
        assertEquals(0, userDao.getCount());

        userDao.add(user1);
        assertEquals(1, userDao.getCount());

        userDao.add(user2);
        assertEquals(2, userDao.getCount());

        userDao.add(user3);
        assertEquals(3, userDao.getCount());
    }
}

deleteAll(), getCount() 예외처리
🟢 예외 발생 원인 : Local에서는 문제가 없지만, 서버 환경에서는 서버가 일찍 Down될 수 있음
👉 ps.close, c.close가 실행되지 않아 계속되어 ps, c가 생성될 수 있음

🟢 try catch문을 사용하여 예외처리
👉 try catch 단축키 : Ctrl + Alt + T

🔴 rs, ps, conn가 null이 아니면 close 처리

public void deleteAll() throws SQLException, ClassNotFoundException {
        Connection conn = null;
        PreparedStatement ps = null;

        try{
            conn = connectionMaker.getConnection();
            ps = conn.prepareStatement("DELETE FROM users");
            ps.executeUpdate();
        } catch (SQLException e) {
            throw e;
        } finally {     // error가 나도 실행되는 블럭
            if(ps != null) {
                try {
                    ps.close();
                } catch (SQLException e) {
                }
            }
            if(conn != null){
                try {
                    conn.close();
                } catch (SQLException e) {
                }
            }
        }
    }

    public int getCount() throws SQLException, ClassNotFoundException {
        Connection conn = null;
        PreparedStatement ps = null;
        ResultSet rs = null;

        try {
            conn = connectionMaker.getConnection();

            ps = conn.prepareStatement("SELECT count(*) FROM users");

            rs = ps.executeQuery();
            rs.next();
            return rs.getInt(1);
        } catch (SQLException e) {
            throw e;
        } finally {
            if(rs != null){
                try {
                    rs.close();
                } catch (SQLException e) {
                }
            }
            if(ps != null) {
                try {
                    ps.close();
                } catch (SQLException e) {
                }
            }
            if(conn != null){
                try {
                    conn.close();
                } catch (SQLException e) {
                }
            }
        }
    }

0개의 댓글