JAVA 웹개발 과정 국비 24일차 정리

24일차

※ 배우는 과정이므로 정확하지 않을 수 있습니다.
잘못된 점은 알려주시면 수정하도록 하겠습니다!


오늘은 실습문제를 풀었기 때문에 정리할 게 많이 없다.

1. List 클래스

그 동안 아래와 같이 ArrayList 선언을 많이 해줬었는데, MenuDTO의 ArrayList보다는 List로 더 많이 사용한다.

ArrayList<MenuDTO> result = new ArrayList<>();
List<MenuDTO> result = new ArrayList<>();

2. DTO란?

DTO(Data Transfer Object)란?
이전 Java 실습 때 맛보기로 MVC 패턴을 연습했을 때의 cafeMenu, student 클래스
데이터를 받아서 전달하는게 목적이다.

3. 실습문제 - 방명록 시스템

실습문제를 푸는 중, 어려웠던 부분이 있어서 정리한다.
방명록 시스템 예제 중 방명록 검색하기 기능을 구현하는 부분이다.

  1. ID로 검색하는 방법
  2. 작성자로 검색하는 방법

두 가지 방법 중 1. ID로 검색하는 방법을 정리한다.

1) 내가 했던 방법

ArrayList에 MenuDTO를 넣는 방법으로만 실습을 해서 그런지 아무생각없이 동일하게 코드를 짰다.
그런데 ID는 PRIMARY KEY 설정이 되어있어서 2개 이상의 결과값을 보여주지 않는다는 특성이 있다.
따라서 ArrayList 를 쓰는 것은 비효율적이다.
여기까지의 힌트를 듣고 혼자 코드를 만들어보았지만, 정상적인 ID를 입력하지 않았을 시에 리턴하는 값을 어떤걸로 두어야 할지 몰라서 결국 풀지 못했다.
그리고 1개 또는 0개의 값으로 정해져있기 때문에, while문 대신 if문을 사용하는 것이 좋다.

2) return null

내가 고민했던 문제를 한번에 해결해 줄 수 있는 방법이다.
바로 ResultSet이 아무것도 데이터를 가져오지 못한다면, null을 리턴하는 것이다.
이렇게 하면 Main에서 dto의 값이 null일 때, 대상을 찾지 못했다고 안내문구를 출력할 수 있다.

// MessageDAO Class
public MessageDTO search(int id) throws Exception {
	Connection con = getConnection();
	Statement stat = con.createStatement();
	String sql = "SELECT * FROM Message WHERE seq=" + id;
	ResultSet rs = stat.executeQuery(sql);
	
	if(rs.next()) {
		int seq = rs.getInt("seq");
		String writer = rs.getString("writer");
		String message = rs.getString("message");
		
		MessageDTO dto = new MessageDTO(seq, writer, message);
		con.close();
		return dto;
	}
	con.close();
	return null;
}
// Main Class
if(srchMenu.equals("1")) {
	System.out.print("검색할 ID : ");
	int id = Integer.parseInt(sc.nextLine());
	MessageDTO dto = dao.search(id);
	
	if(dto == null) {
		System.out.println("찾는 대상의 정보가 없습니다.");
	}else {
		System.out.println("SEQ\tWRITER\tMESSAGE");
		
		System.out.println(dto.getSeq()+"\t"+
					dto.getWriter()+"\t"+dto.getMessage());
		
	}
	
}

3) if문 밖에서의 MessageDTO 변수 선언

위의 코드는 con.close(); 를 두번 해주는 것이 어딘가 불편하다.
2번 코드의 문제점은 dto 변수를 if문 안쪽에다 만들어서 바깥으로 변수를 가져갈 수 없다는 것이다.
따라서 if문 바깥쪽에 변수를 선언해주면 된다.
con.close코드의 중복도 해결할 수 있다.
Main에서의 동작 코드는 동일하다.

// MessageDAO Class
public MessageDTO search(int id) throws Exception {
	Connection con = getConnection();
	Statement stat = con.createStatement();
	String sql = "SELECT * FROM Message WHERE seq=" + id;
	ResultSet rs = stat.executeQuery(sql);
	
	MessageDTO dto = null;
	if(rs.next()) {
		int seq = rs.getInt("seq");
		String writer = rs.getString("writer");
		String message = rs.getString("message");
		dto = new MessageDTO(seq, writer, message);
	}
	con.close();
	return dto;	
}
// Main Class
if(srchMenu.equals("1")) {
	System.out.print("검색할 ID : ");
	int id = Integer.parseInt(sc.nextLine());
	MessageDTO dto = dao.search(id);
	
	if(dto == null) {
		System.out.println("찾는 대상의 정보가 없습니다.");
	}else {
		System.out.println("SEQ\tWRITER\tMESSAGE");
		
		System.out.println(dto.getSeq()+"\t"+
					dto.getWriter()+"\t"+dto.getMessage());	
	}
}

4) 변수 dto에 새로운 dto객체를 담으면?

그렇다면 null 말고 new를 통해 새로운 인스턴스를 담으면 어떨까?
if문 안쪽에서 생성자로 새로운 인스턴스를 생성할 수 없기 때문에 setter를 통해 값을 넣어줘야 한다.
만약 값이 없다면 빈 인스턴스를 리턴하므로 seq의 값이 비어있는 0으로 저장되어있을 것이고, 따라서 main에서의 if문도 바뀌어야한다.

// MessageDAO Class
public MessageDTO search(int id) throws Exception {
	Connection con = getConnection();
	Statement stat = con.createStatement();
	String sql = "SELECT * FROM Message WHERE seq=" + id;
	ResultSet rs = stat.executeQuery(sql);
	
	MessageDTO dto = new MessageDTO();
	
	if(rs.next()) {
		int seq = rs.getInt("seq");
		String writer = rs.getString("writer");
		String message = rs.getString("message");
		// setter 사용
		dto.setSeq(seq);
		dto.setWriter(writer);
		dto.setMessage(message);

	}
	con.close(); 
	return dto;
}
// Main Class
if(srchMenu.equals("1")) {
	System.out.print("검색할 ID : ");
	int id = Integer.parseInt(sc.nextLine());
	MessageDTO dto = dao.search(id);
	
	if(dto.getSeq() == 0) {
		System.out.println("찾는 대상의 정보가 없습니다.");
	}else {
		System.out.println("SEQ\tWRITER\tMESSAGE");
		
		System.out.println(dto.getSeq()+"\t"+
					dto.getWriter()+"\t"+dto.getMessage());
	}
}

4. PreparedStatement 클래스

Statement 보다 발전한 Statement 를 상속 받는 클래스.
편의성 / 쿼리 실행 속도 (성능) / 보안성이 뛰어나다.
대부분의 경우 PreparedStatement 클래스를 사용하지만 성능적인 측면에서 아주 가~끔 Statement를 사용하는 경우도 존재한다.

1) 기존 Statement Class 사용

기존 Statement 클래스는 createStatement 메소드를 사용하여 stat 변수를 선언하고, 후에 sql문을 만들어서 result에 담아주었다.

// 기존 Statement Class 사용
Statement stat = con.createStatement();
String sql = "INSERT INTO cafe_menu VALUES(cafe_menu_seq.NEXTVAL,'"+name+"',"+price+")";
int result = stat.executeUpdate(sql);

2) pstat 변수 선언

그러나 PreparedStatement 는 바로 sql문을 파라미터로 받을 수 있기 때문에 아래와 같이 사용한다.

// PreparedStatement Class 사용
String sql = "INSERT INTO cafe_menu VALUES(cafe_menu_seq.NEXTVAL,'"+name+"',"+price+")";
PreparedStatement psat = con.prepareStatement(sql);

3) 입력값 sql문에 삽입하기

sql문을 바로 파라미터로 받을 수 있기 때문에 sql문에 있는 변수 name 과 price 를 적절하게 문법을 바꿔주어야 한다.

입력할 변수들을 모두 물음표(?)로 바꿔주고,
자료형에 맞춰 set메소드를 사용하여 첫번째 파라미터에 몇번째 물음표에 들어갈지, 두번째 파라미터에는 어떤 변수가 들어갈지 작성해준다.

String sql = "INSERT INTO cafe_menu VALUES(cafe_menu_seq.NEXTVAL,?,?)";
PreparedStatement pstat = con.prepareStatement(sql);
pstat.setString(1, name);  // 1번 물음표에 name값을 할당
pstat.setInt(2, price);  // 2번 물음표에 price값을 할당

4) 동일한 리턴값

리턴값은 기존 Statement와 같이 int형으로 리턴한다.
주의할 점은 이때 executeUpdate 메소드에 sql을 넣으면 안된다.

int result = pstat.executeUpdate();

5) 최종 변경 코드

// 기존 Statement Class 사용
Statement stat = con.createStatement();
String sql = "INSERT INTO cafe_menu VALUES(cafe_menu_seq.NEXTVAL,'"+name+"',"+price+")";
int result = stat.executeUpdate(sql);
// PreparedStatement Class 사용
String sql = "INSERT INTO cafe_menu VALUES (cafe_menu_seq.NEXTVAL,?,?)";
PreparedStatement pstat = con.preparedStatement(sql);
pstat.setString(1, name);
pstat.setInt(2, price);
int result = pstat.executeUpdate();

댓글남기기