BackEnd/Spring & Springboot Study

[토비의 스프링] 1.1 초난감 DAO

1.1.1 User

package springbook.user.domain;

public class User {
	String id;
	String name;
	String password;
	
	public String getId() {
		return id;
	}
	public void setId(String id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = password;
	}
}

/*
사용자 정보를 저장할 때는 자바빈 규약을 따르는 오브젝트를 이용하면 편리하다.
사용자 정보를 저장할 User 클래스를 만든다.
id, name, password 세 개의 프로퍼티를 가진 User클래스
*/


User 오브젝트가 담긴 정보가 실제로 보관될 DB의 테이블 생성하기

필드명  타입  설정
Id VARCHAR(10) Primary key
Name VARCHAR(20) Not Null
Password  VARCHAR(20)  Not Null

특정 DB에 종속적이지 않으므로 사용하기 편한 DB를 이용해도 좋다라고 책이 적혀있어서 일단 Mysql 8버전으로 진행하려고 한다. 버전 문제가 발생하면 사용해본적 없는 Docker에 Mysql 5.1버전을 적재시켜서 사용해 봐야겠다.

더보기

 

CREATE DATABASE IF NOT EXISTS 'springbook'

USE 'springbook'

create table users {

    id varchar(10) primary key,

    name varchar(20 not null,

    password varchar(10) not null

}

위 쿼리문을 사용해 users 테이블을 생성하였다.


자바빈
디폴트 생성자와 프로퍼티 관례를 따라 만들어진 오브젝트

더보기

디폴트 생성자 : 파라미터가 없는 디폴트 생성자를 갖고 있어야한다. 툴이나 프레임워크에서 리플렉션을 이용해 오브젝트를 생성하기 때문에 필요하다.
프로퍼티: 자바빈이 노출하는 이름을 가진 속성을 프로퍼티라고 한다. 프로퍼티는 set으로 시작하는 수정자 메소드(setter)와 get으로 시작하는 접근자 메소드(getter)를 이용해 수정 또는 조회할 수 있다.

 

1.1.2 UserDao

 

사용자 정보를 DB에 넣고 관리할 수 있는 DAO 클래스를 만들어보자.

일단 사용자를 생성, 아이디로 사용자 정보를 읽어오는 두 개의 메소드를 먼저 만들어보자.

 

JDBC 이용을 위한 작업의 일반적인 순서

  • DB 연결을 위한 Connection을 가져온다.
  • SQL을 담은 Statement(또는 PreparedStatement)를 만든다.
  • 만들어진 Statement를 실행한다.
  • 조회의 경우 SQL 쿼리의 실행 결과를 ResultSet으로 받아서 정보를 저장할 오브젝트에 옮겨준다.
  • 작업 중에 생성된 Connection, Statement, ResultSet 같은 리소스는 작업을 마친 후 반드시 닫아준다.
  • JDBC API가 만들어내는 예외(exception)를 잡아서 직접 처리하거나, throws를 선언해서 예외가 발생시 메소드 밖으로 던지게 한다.

JDBC를 이용한 등록과 조회 기능이 있는 UserDao 클래스

더보기
package springbook.user.domain;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class UserDao {
    // DB 관련 상수
	/*
	private static final String DRIVER = "com.mysql.cj.jdbc.Driver";
    private static final String URL = "jdbc:mysql://localhost/springbook?serverTimezone=Asia/Seoul&useSSL=false&characterEncoding=utf8&allowPublicKeyRetrieval=true&useSSL=false";
    private static final String USER = "spring";
    private static final String PASSWORD = "book";
	*/
	
	public void add(User user) throws ClassNotFoundException, SQLException {
		Class.forName("com.mysql.cj.jdbc.Driver");
		Connection c = DriverManager.getConnection("jdbc:mysql://localhost/springbook?serverTimezone=Asia/Seoul&useSSL=false&characterEncoding=utf8&allowPublicKeyRetrieval=true&useSSL=false", "spring", "book");
		
		PreparedStatement ps = c.prepareStatement("insert into users(id, name, password) values(?,?,?)");
		ps.setString(1, user.getId());
		ps.setString(2, user.getName());
		ps.setString(3, user.getPassword());
		
		ps.executeUpdate();
		
		ps.close();
		c.close();
	}
	
	public User get(String id) throws ClassNotFoundException, SQLException {
		Class.forName("com.mysql.cj.jdbc.Driver");
		Connection c = DriverManager.getConnection("jdbc:mysql://localhost/springbook?serverTimezone=Asia/Seoul&useSSL=false&characterEncoding=utf8&allowPublicKeyRetrieval=true&useSSL=false", "spring", "book");
		
		PreparedStatement ps = c.prepareStatement("select * from users where id = ?");
		ps.setString(1, id);
		
		ResultSet rs = ps.executeQuery();
		rs.next();
		User user = new User();
		user.setId(rs.getString("id"));
		user.setName(rs.getString("name"));
		user.setPassword(rs.getString("password"));
		
		rs.close();
		ps.close();
		c.close();
		
		return user;
	}
}

 

 

1.1.3 main()을 이용한 DAO 테스트 코드

 

클래스 동작 확인을 위해 일반적으로 웹 어플리케이션을 만들어 서버에 배치, 웹 브라우저를 통해 DAO 기능을 사용해보는 것이 일반적이나 배보다 배꼽이 더 크다.

그래서 main() 메서드를 활용하여 DAO의 코드를 테스트 해보자.

 

만들어진 코드의 기능을 검증하고 할 때 사용할 수 있는 가장 간단한 방법은 오브젝트 스스로 자신을 검증하도록 만들어 주는 것이다.

모든 클래스에는 자신을 엔트리 포인트로 설정해 직접 실행이 가능하게 해주는 스태틱 메소드 main()이 있지 않은가.

main 메소드를 만들고 그 안에서 UserDao의 오브젝트를 생성해 add()와 get() 메소드를 검증해보자.

 

1. User 오브젝트 생성 및 프로퍼티에 값을 넣고 add() 메소드를 이용하여 DB에 등록

2. get() 메소드를 사용해 DB에 저장된 결과를 가져와 확인

 

리스트 1-3 테스트용 main()  메소드

더보기
package springbook.user.domain;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class UserDao {
    // DB 관련 상수
	/*
	private static final String DRIVER = "com.mysql.cj.jdbc.Driver";
    private static final String URL = "jdbc:mysql://localhost/springbook?serverTimezone=Asia/Seoul&useSSL=false&characterEncoding=utf8&allowPublicKeyRetrieval=true&useSSL=false";
    private static final String USER = "spring";
    private static final String PASSWORD = "book";
	*/
	public static void main(String[] args) throws ClassNotFoundException, SQLException {
		UserDao dao = new UserDao();
		
		User user = new User();
		user.setId("whiteship");
		user.setName("백기선");
		user.setPassword("married");
		
		dao.add(user);
		
		System.out.println(user.getId() + " 등록 성공");
		
		User user2 = dao.get(user.getId());
		System.out.println(user2.getName());
		System.out.println(user2.getPassword());
		
		System.out.println(user2.getId());
	}
	
	
	public void add(User user) throws ClassNotFoundException, SQLException {
		Class.forName("com.mysql.cj.jdbc.Driver");
		Connection c = DriverManager.getConnection("jdbc:mysql://localhost/springbook?serverTimezone=Asia/Seoul&useSSL=false&characterEncoding=utf8&allowPublicKeyRetrieval=true&useSSL=false", "spring", "book");
		
		PreparedStatement ps = c.prepareStatement("insert into users(id, name, password) values(?,?,?)");
		ps.setString(1, user.getId());
		ps.setString(2, user.getName());
		ps.setString(3, user.getPassword());
		
		ps.executeUpdate();
		
		ps.close();
		c.close();
	}
	
	public User get(String id) throws ClassNotFoundException, SQLException {
		Class.forName("com.mysql.cj.jdbc.Driver");
		Connection c = DriverManager.getConnection("jdbc:mysql://localhost/springbook?serverTimezone=Asia/Seoul&useSSL=false&characterEncoding=utf8&allowPublicKeyRetrieval=true&useSSL=false", "spring", "book");
		
		PreparedStatement ps = c.prepareStatement("select * from users where id = ?");
		ps.setString(1, id);
		
		ResultSet rs = ps.executeQuery();
		rs.next();
		User user = new User();
		user.setId(rs.getString("id"));
		user.setName(rs.getString("name"));
		user.setPassword(rs.getString("password"));
		
		rs.close();
		ps.close();
		c.close();
		
		return user;
	}
}

https://mvnrepository.com/artifact/mysql/mysql-connector-java/8.0.21

오랜만에 src에 java build path로 external library를 직접 걸어줘서 사용한다.

GRANT ALL PRIVILESGES ON *.* TO spring@localhost IDENTIFIED BY 'book with grant option;

위 구문이 버전문제 때문인지 먹히지 않아 아래와 같이 유저를 선언했다.

create user 'spring'@'localhost' identified by 'book';

grant all privileges on *.* to 'spring'@'localhost';

FLUSH PRIVILEGES;

 

실행결과는

whiteship 등록 성공
백기선
married
whiteship

 

이 UserDao 클래스 코드에는 여러가지 문제가 있다. 실제 프로젝트에서 이렇게 짜면 쫓겨나지 않을까 한심한 코드이다.이제 이 문제 많은 초난감 DAO코드를 객체지향 기술의 원리에 충실한 멋진 스프링 스타일 코드로 개선해보자.그런데 이 코드는 돌아가니까 사실 괜찮은 코드가 아닐까? 잘 돌아가는 코드를 고치는 이유는? 코드를 개선했을때 장점은?당장, 미래에 주는 이득은? 객체지향 설계의 원칙과는 무슨 상관이 있을까?스프링을 공부한다는 건 이런 문제 제기와 의문에 대한 답을 찾아나가는 과정이다.스프링을 이용해 개발자 스스로가 만들어내는 것이지, 스프링이 주는것이 아니다.