본문 바로가기

순이코딩/Java

[Java] JDBC 2(MVC를 활용)

728x90
반응형

저번에 JDBC에 대해서 공부했으니 본격적으로 들어가기 전에 먼저 MVC에 대해 알아보겠습니다.

 

1. MVC

 

MVC란 디자인패턴 중 하나입니다. 이전에 좀 더 실생활에 맞는 객체 지향 프로그래밍을 해야 한다고 했었는데 이를 위해 만든 패턴입니다.

MVC는 Model, View, Controller의 약자입니다. 쉽게 말해 하나의 프로그램을 구현하기 위해 3 클래스에 각각의 역할을 담아 나누었다는 뜻입니다. 

 

[Java] mp3player 프로그램 만들기

이전시간에 공부했던 내용을 바탕으로 mp3 player를 구현한 프로그램을 만들었습니다. 처음엔 Mp3와 Mp3Main 2개의 클래스로만 나누었지만 하다 보니 비효율적인 거 같아 마음대로 나누었습니다. 먼

soonart.tistory.com

 

MVC모델

 

 

1) Model

 

Model은 이전 객체 지향 프로그래밍에 대해 배웠을 때 설계도 클래스와 같은 역할입니다.

 

[Java] 객체 지향 프로그래밍

절차 지향 프로그래밍은 실행하고자 하는 절차를 정하고, 이 절차대로 프로그래밍하는 방법입니다. 그러나 객체 지향 프로그래밍은 프로그램을 보다 실제 세상에 가깝게 모델링하여 실제 세상

soonart.tistory.com

그렇기 때문에 다루고자 하는 모든 데이터를 가지고 있습니다.

(Mp3Player구현 당시 Mp3클래스와 같은 역할입니다. 필드에 다루고자 하는 데이터인 노래제목, 가수, 재생시간, 경로를 넣었고, 생성자 메소드 그리고 private 접근제한을 했기 때문에 다른 클래스에서 접근할 수 있도록 getter메소드를 사용했습니다.)

 

2) View

 

뷰는 단순하게 사용자가 이용하는 것이라고 생각하면 됩니다. 예를 들어 실제 Mp3플레이어를 사용한다고 할 때, 사용자는 어떠한 원리로 '▶'과 같은 버튼을 누르면 다음곡이 재생되는지 알 필요가 없습니다. 그저 어떤 버튼을 누르면 뭐가 실행되고 어떠한 값을 입력하면 뭐가 출력되는지 등 보여지는 것만 알면 됩니다. View 클래스는 이름과 같이 보이는 클래스라고 생각하면 됩니다.

이전에 설명했던 Main 클래스와 같습니다. 단지 이전에 클래스를 2개로 나누었을 때는 Model과 View+Controller 이렇게 나뉘었습니다. Mp3 Player글에서는 Mp3Main 클래스와 같습니다. 

			case 1:
				m = p.play();
				System.out.println(mInfo(m));
				break;

해당 클래스의 코드 일부분과 같이, 1의 경우엔 플레이하고 창 출력한다는 보여지는 내용만 가지고 있습니다.

 

3) Controller

 

컨트롤러는 모델과 뷰의 중간다리 역할입니다. 모델은 사용하고자 하는 데이터를 가지고 있고, 뷰는 사용자에게 보여주는 역할입니다. 그렇다면 중간다리에서는 가지고 있는 데이터를 어떻게 활용하여 사용자에게 원하는 결과를 보여줄 수 있는지 기능하는 역할을 해야합니다.

MP3Player 클래스와 같습니다. 해당 클래스에서는 Model(Mp3클래스)에서 가지고 있는 필드와 메소드를 이용해 리스트를 생성했고, 각각 play메소드, nextPlay 메소드 등 여러 메소드를 만들었습니다. 그리고 이 리스트와 메소드를 이용해 View(Mp3Main클래스)에서는 사용자에게 1을 누르면 재생한다는 단순한 결과를 만들어내고 있습니다.

 

 


 

이젠 이걸 JDBC에 활용해 보겠습니다. 그전에 DAO와 DTO에 대해 알아보겠습니다.

DAO(Data Access Object)는 Data에 접근하기 위한 객체로 DB에 실제로 접근하는 객체입니다.

DTO(Data Transfer Object)는 계층 데이터 교환을 하기 위해 사용하는 개체로 로직을 가지지 않는 순수한 객체입니다.

 

이전시간에 했던 insert를 4개의 클래스에 나누어 보겠습니다. 클래스는 각각 DTO, View, Controller, DAO입니다.

DTO는 MVC로 따지면 Model이라 할 수 있고 위의 MP3프로그램 과는 다르게 데이터서버에 접근하기 때문에 MVC 3 클래스가 아닌 4 클래스입니다.

 

이전 시간에는 데이터값을 직접 입력해서 테이블에 튜플을 추가했지만 여기서는 사용자에게 값을 입력받아 튜플을 추가해보도록 하겠습니다.

먼저 DTO클래스입니다. 여기서는 필드와 메서드만 생성하면 손댈 일이 없습니다. View, Controller, DAO 간에 데이터 교환을 위해 만든 것이기 때문에 중간에 전달하는 역할을 할 뿐이기 때문입니다.

package insert;

public class MemberDTO {

	// 필드 - ID, PW, NAME, AGE
	private String id;
	private String pw;
	private String name;
	private int age;

	// 생성자 메소드
	public MemberDTO(String id, String pw, String name, int age) {
		super();
		this.id = id;
		this.pw = pw;
		this.name = name;
		this.age = age;
	}

	// getter, setter 메소드
	public String getId() {
		return id;
	}
	public void setId(String id) {
		this.id = id;
	}
	public String getPw() {
		return pw;
	}
	public void setPw(String pw) {
		this.pw = pw;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
}

MEMBER라는 테이블에 넣을 쿼리의 데이터 변수들(필드), 생성자 메소드, getter/setter메소드만 넣어주고 끝입니다. 이후로는 이 클래스는 손댈 일이 없습니다.

다음으로는 뷰클래스입니다. (여기부터는 클래스를 오가며 순서별로 잘라서 설명하겠습니다.)

		Scanner sc = new Scanner(System.in);
		System.out.print("ID : ");
		String id = sc.next();
		System.out.print("PW : ");
		String pw = sc.next();
		System.out.print("NAME : ");
		String name = sc.next();
		System.out.print("AGE : ");
		int age = sc.nextInt();

		MemberDTO dto = new MemberDTO(id, pw, name, age);

먼저 우리가 DB에 저장할 데이터값을 사용자에게 입력받아야 합니다. 각 쿼리의 데이터값들을 id, pw, name, age 변수에 담고 이 변수들을 하나의 객체로 묶습니다. 그러기 위해 DTO클래스에서 만든 생성자 메소드를 사용했습니다.

이번에는 컨트롤 클래스입니다.

	public void insert(MemberDTO dto) {
		MemberDAO dao = new MemberDAO();
		dao.insert(dto);
}

컨트롤 클래스는 뷰 클래스와 DTO 클래스의 중간다리 역할입니다. 컨트롤 클래스에서 dto값을 받는 것은 DAO에 전달하기 위해서 뿐이기 때문에 dto 값을 사용하는 메서드만 만들어 줍니다.

다음은 DTO클래스입니다.

1) 먼저 dto 값을 받는 메소드를 생성했습니다. 그리고 DB에 연결하기 위한 Connection 객체와 전송하기 위한 PreparedStatement 객체를 선언했습니다. 

	public void insert(MemberDTO dto) {

		Connection conn = null;
		PreparedStatement psmt = null;

2) 다음은 순서대로 동적 로딩, DB 연결, SQL문 실행입니다. 

여기서 try는 catch와 함께 쓰이면 try문 안에 있는 코드 중 오류가 발생했을 때 catch가 잡고 printStackTrace()를 통해 어떤 런타임 오류가 발생했는지 알려줍니다. 런타임오류와 try문은 다음 글에서 짧게 다루도록 하겠습니다.

어쨌든 DTO를 활용해 뷰에서 입력받은 값을 컨트롤로 컨트롤에서 DAO로 가져와 전송까지 진행했습니다.

		try {
			Class.forName("oracle.jdbc.driver.OracleDriver");

			String url = "jdbc:oracle:thin:@localhost:1521:xe";
			String db_id = "SERVICE";
			String db_pw = "12345";
			conn = DriverManager.getConnection(url, db_id, db_pw);

			String sql = "insert into MEMBER values(?, ?, ?, ?)"; // 바인딩 변수 : ?
			psmt = conn.prepareStatement(sql);

			psmt.setString(1, dto.getId());
			psmt.setString(2, dto.getPw());
			psmt.setString(3, dto.getName());
			psmt.setInt(4, dto.getAge());

			cnt = psmt.executeUpdate();

		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (SQLException e) {
			e.printStackTrace();
		}

3) DAO의 전체 코드입니다. 위의 코드와 다르게 추가된 부분이 있습니다.

먼저 int cnt가 선언과 동시에 초기화되고 try문 안에서 psmt.executeUpdate()로 다시 초기화되었습니다. 그리고 return값은 변수 cnt이며 return이 생기며 insert메서드의 데이터 타입은 void에서 int로 바뀌었습니다.  executeUpdate()는 실행된 개수만큼의 정수를 반환하기 때문에 사용자에게 결과를 출력해 주기 위해 사용되었습니다.

두 번째로 추가된 것은 finally입니다. 중간에 오류가 생겼다고 해서 연결되었던 DB를 그대로 닫지 않고 끝낼 수는 없기 때문에 위에서 오류가 생겨서 이후 실행되지 않더라도 이것만은 실행하겠다는 뜻입니다.

닫는 코드도 만약 앞에서 열리지 않았다면 실행될 필요 없으니 다시 try문안에 들어갔습니다. 

	public int insert(MemberDTO dto) {

		Connection conn = null;
		PreparedStatement psmt = null;
		int cnt = 0;  // 추가

		try {
			Class.forName("oracle.jdbc.driver.OracleDriver");

			String url = "jdbc:oracle:thin:@localhost:1521:xe";
			String db_id = "SERVICE";
			String db_pw = "12345";
			conn = DriverManager.getConnection(url, db_id, db_pw);

			String sql = "insert into MEMBER values(?, ?, ?, ?)"; // 바인딩 변수 : ?
			psmt = conn.prepareStatement(sql);

			psmt.setString(1, dto.getId());
			psmt.setString(2, dto.getPw());
			psmt.setString(3, dto.getName());
			psmt.setInt(4, dto.getAge());

			cnt = psmt.executeUpdate(); //추가

		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (SQLException e) {
			e.printStackTrace();
		} finally { //추가
			try {
				psmt.close();
				conn.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}

		}
		return cnt;

다시 컨트롤러 클래스입니다. 여기선 DAO에서 cnt값을 리턴 받고 있습니다. 이 cnt값을 이용해 뷰 클래스로 가기 전에 if문을 사용해 cnt가 1보다 크다면( insert가 진행되지 않았다면 0 값을 반환하고, 진행되었다면 그 개수만큼 1 이상의 값을 반환하기 때문)"insrt 성공"이라는 문구를 출력, 0이거나 작으면 "insert 실패"라는 문구를 출력합니다.

package insert;

public class Controller {

	public void insert(MemberDTO dto) {
		MemberDAO dao = new MemberDAO();
		int cnt = dao.insert(dto);
		if(cnt >0) {
			System.out.println("insert 성공");
		}else {
			System.out.println("insert 실패");
		}
	}
}

마지막으로 다시 뷰클래스입니다. 뷰 클래스도 마지막에 코드가 추가되었습니다. 컨트롤러를 import 하고 컨트롤러의 insert 메서드를 반환해 성공, 실패를 출력합니다.

package insert;

import java.util.Scanner;

public class View {

	public static void main(String[] args) {

		Scanner sc = new Scanner(System.in);
		System.out.print("ID : ");
		String id = sc.next();
		System.out.print("PW : ");
		String pw = sc.next();
		System.out.print("NAME : ");
		String name = sc.next();
		System.out.print("AGE : ");
		int age = sc.nextInt();

		MemberDTO dto = new MemberDTO(id, pw, name, age);
		
		Controller con = new Controller();
		con.insert(dto);
	}
}

 

위의 과정을 그림으로 표현하면 이와 같습니다. 여러 클래스로 나누어 작성한다고 한 번에 하려 하지 말고 과정 순서대로 순서대로 하면 전혀 복잡하지 않습니다. 

728x90
반응형