MVC
SW공학 아키텍처 디자인 패턴 중 하나로 Model View Controller 의 약자 MVC 줄여 말하는 패턴
MVC의 핵심은 화면 (View), 제어 (Controller), 데이터 연동 ( 을 구성하는 로직 코드 를 분리하여
서로간의 영향도를 줄이고 코드를 간결하게 관리하여 코드 설계 품질을 향상시킬 수 있는 기법
한국에서만 주로 사용하는 구조
해외에선 자바의 지분률이 낮다
패키지 구조
◆ View : 프론트 UI
◆ Controller : 뷰와 모델을 적절히 제어, View 로 부터 전달 받은 사용자의 입력 요청 을 분석하고 ,
이에 필요한 데이터를 Model 로 부터 전달 받아 올바른 결과의 View 를 구성하도록 전달만 수행
◆ Model : 백엔드, DBMS, 데이터 처리 가공 (비지니스 로직)
Model 데이터 객체
VO
DTO
DAO
현업에서는 사실 VO와 DTO를 크게 구분하지 않음
JDBC 공통 모듈 구현
DBMS연동 , 객체반환 , 트렌잭션 처리 등 중복 코드를 새로운 클래스에서 구동될 수 있게
연동 구조 재설계
Driver.properties
프로그램이 가지는 기본 정보들을 properties 확장자 파일로 저장해 놓으면
유지보수에 용이 함
Ex)
#Driver.properties
driverClass = com.mysql.cj.jdbc.Driver
url = jdbc:mysql://localhost:3306/
user=user
pwd=1212
Common
클래스 내부의 공통적이지만 , 중복 된 코드를 처리하는 클래스가 담겨있는 패키지로
Connection 생성 , Connection/ PreparedStatement 반환 메소드 ,
트랜잭션 (commit, 등의 공통 기능을 가짐
Service
Connection class관리 및 객체 반환 , 트랜젝션 관리하는 패키지
싱글톤 패턴 (Singleton
객체 사용 시 새로운 객체를 계속 생성해서 사용하는 것이 아니라 하나의 객체만
생성하여 공유하는 것
MVC 클래스 구조 (Service 추가)
View는 HTML,CSS, JSP등으로 대체
사진은 Service와 DAO가 1대1 구조라 의미가 없음 (묶어도 됨)
원래는 Service : DAO = 1 : N 구조
위 구조는 나중에 스프링 배울때도 똑같이 갈 예정
JDBC Connection Pool
다수의 사용자 처리를 빠르게 처리하기 위해 다수의 DB Connection 을 미리 생성하고
연결해 놓은 Pool 을 유지하고 , 사용자 요청에 따라 Connection 을 차례로 사용 하는 기법
Pool 순서
1. Connection을 미리 생성해서 보관
2. Connection에 대한 요청이 들어오면 , 보관 중인 Connection 중 하나를 넘겨줌
3. 사용이 끝난 Connection 을 다시 보관
사용 하면 속도가 빨라지고, 자원을 효율적으로 사용할수 있다.
또한 Connection의 객체 수 제어를 하기 편하다
(원리만 알아두자 나중에 Spring Framework에서는 알아서 해준다)
[예시]
[JDBCTmeplate.java]
package common;
import java.io.FileReader;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Properties;
// jdbc 공통부 코드
public class JDBCTemplate {
// 공통부 코드를 작성
// 1. JDBC 관련 드라이버 로드
// 2. connection 생성
// 3. 객체들 close
private static String driverClass;
private static String url;
private static String user;
private static String password;
static {
try {
Properties prop = new Properties(); // 컬랙션에서 배운 Map중 하나
FileReader fr = new FileReader("resources/data-source.properties");
prop.load(fr);
fr.close();
driverClass = prop.getProperty("driverClass");
url = prop.getProperty("url");
user = prop.getProperty("user");
password = prop.getProperty("password");
Class.forName(driverClass);
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
}
public static Connection openConnection() {
Connection conn = null;
try {
conn = DriverManager.getConnection(url, user, password);
conn.setAutoCommit(false); // 자동 커밋 해제
} catch (Exception e) {
e.printStackTrace();
}
return conn;
}
public static void close(Connection conn) {
try {
if(conn != null && conn.isClosed() == false) {
conn.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
public static void close(PreparedStatement pstmt) {
try {
if(pstmt != null && pstmt.isClosed() == false) {
pstmt.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
public static void close(ResultSet rs) {
try {
if(rs != null && rs.isClosed() == false) {
rs.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
public static void commit(Connection conn) {
try {
conn.commit();
} catch (SQLException e) {
e.printStackTrace();
}
}
public static void rollback(Connection conn) {
try {
conn.rollback();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
[Member.java]
package member.model.vo;
import java.util.Date;
public class Member {
private int mno;
private String ID;
private String password;
private String NAME;
private String GENDER;
private int AGE;
private String EMAIL;
private String PHONE;
private String ADDRESS ;
private String HOBBY ;
private Date BIRTHDAY ;
private Date ENROLLDATE;
public Member() {
super();
}
public Member(int mno, String iD, String password, String nAME, String gENDER, int aGE, String eMAIL, String pHONE,
String aDDRESS, String hOBBY, Date bIRTHDAY, Date eNROLLDATE) {
super();
this.mno = mno;
ID = iD;
this.password = password;
NAME = nAME;
GENDER = gENDER;
AGE = aGE;
EMAIL = eMAIL;
PHONE = pHONE;
ADDRESS = aDDRESS;
HOBBY = hOBBY;
BIRTHDAY = bIRTHDAY;
ENROLLDATE = eNROLLDATE;
}
@Override
public String toString() {
return "Member [mno=" + mno + ", ID=" + ID + ", password=" + password + ", NAME=" + NAME + ", GENDER=" + GENDER
+ ", AGE=" + AGE + ", EMAIL=" + EMAIL + ", PHONE=" + PHONE + ", ADDRESS=" + ADDRESS + ", HOBBY=" + HOBBY
+ ", BIRTHDAY=" + BIRTHDAY + ", ENROLLDATE=" + ENROLLDATE + "]";
}
public int getMno() {
return mno;
}
public void setMno(int mno) {
this.mno = mno;
}
public String getID() {
return ID;
}
public void setID(String iD) {
ID = iD;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getNAME() {
return NAME;
}
public void setNAME(String nAME) {
NAME = nAME;
}
public String getGENDER() {
return GENDER;
}
public void setGENDER(String gENDER) {
GENDER = gENDER;
}
public int getAGE() {
return AGE;
}
public void setAGE(int aGE) {
AGE = aGE;
}
public String getEMAIL() {
return EMAIL;
}
public void setEMAIL(String eMAIL) {
EMAIL = eMAIL;
}
public String getPHONE() {
return PHONE;
}
public void setPHONE(String pHONE) {
PHONE = pHONE;
}
public String getADDRESS() {
return ADDRESS;
}
public void setADDRESS(String aDDRESS) {
ADDRESS = aDDRESS;
}
public String getHOBBY() {
return HOBBY;
}
public void setHOBBY(String hOBBY) {
HOBBY = hOBBY;
}
public Date getBIRTHDAY() {
return BIRTHDAY;
}
public void setBIRTHDAY(Date bIRTHDAY) {
BIRTHDAY = bIRTHDAY;
}
public Date getENROLLDATE() {
return ENROLLDATE;
}
public void setENROLLDATE(Date eNROLLDATE) {
ENROLLDATE = eNROLLDATE;
}
}
[MemberDao.java]
package member.model.dao;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import common.JDBCTemplate;
import member.model.vo.Member;
// static import -> 임포트된 클래스를 마치 자신의 메소드처럼 활용할수 있다.
import static common.JDBCTemplate.*;
public class MemberDao {
/**
■ DAO 설계 지침
1. 기본적으로 관련 Table과 1:1 매핑하는게 일반적 설계
단, 기능에 따라 다중 Table을 다루기도 함 ex) BoardDao = board + reply + favorites
2. 쿼리 외의 부가기능은 서비스나 JDBC Template로 이관
*/
public List<Member> selectAll(Connection conn){
List<Member> list = new ArrayList<>();
try {
String sql = "SELECT * FROM MEMBER ORDER BY MNO";
PreparedStatement pstmt = conn.prepareStatement(sql);
ResultSet rs = pstmt.executeQuery();
while(rs.next()) {
int mno = rs.getInt("mno");
String id = rs.getString("id");
String password = rs.getString("password");
String name = rs.getString("name");
String gender = rs.getString("gender");
int age = rs.getInt("age");
String email = rs.getString("email");
String phone = rs.getString("phone");
String address = rs.getString("address");
String hobby = rs.getString("hobby");
Date birthday = rs.getDate("birthday");
Date enrolldate = rs.getTimestamp("enrolldate");
Member m = new Member(mno, id, password, name, gender, age, email, phone, address, hobby, birthday, enrolldate);
list.add(m);
}
close(pstmt);
} catch (Exception e) {
e.printStackTrace();
}
return list; // 만일 실패해도 빈리스트가 나가는게 유리하다.
}
public Member selectOne(Connection conn, String memberId) {
Member m = null;
try {
String sql = "SELECT * FROM MEMBER WHERE ID = ?";
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setString(1, memberId);
ResultSet rs = pstmt.executeQuery();
while(rs.next()) {
int mno = rs.getInt("mno");
String id = rs.getString("id");
String password = rs.getString("password");
String name = rs.getString("name");
String gender = rs.getString("gender");
int age = rs.getInt("age");
String email = rs.getString("email");
String phone = rs.getString("phone");
String address = rs.getString("address");
String hobby = rs.getString("hobby");
Date birthday = rs.getDate("birthday");
Date enrolldate = rs.getTimestamp("enrolldate");
m = new Member(mno, id, password, name, gender, age, email, phone, address, hobby, birthday, enrolldate);
}
close(pstmt);
} catch (Exception e) {
e.printStackTrace();
}
return m;
}
// like절로
public List<Member> selectByName(Connection conn, String memberName) {
List<Member> list = new ArrayList<>();
try {
String sql = "SELECT * FROM MEMBER WHERE NAME LIKE ?";
PreparedStatement pstmt = conn.prepareStatement(sql);
// like절 % 붙이는 방법 : setString 메소드 인자 안에서 '%' 붙여서 '%길동%'를 만든다.
pstmt.setString(1, "%" + memberName + "%"); // 홍길동
ResultSet rs = pstmt.executeQuery();
while(rs.next()) {
int mno = rs.getInt("mno");
String id = rs.getString("id");
String password = rs.getString("password");
String name = rs.getString("name");
String gender = rs.getString("gender");
int age = rs.getInt("age");
String email = rs.getString("email");
String phone = rs.getString("phone");
String address = rs.getString("address");
String hobby = rs.getString("hobby");
Date birthday = rs.getDate("birthday");
Date enrolldate = rs.getTimestamp("enrolldate");
Member m = new Member(mno, id, password, name, gender, age, email, phone, address, hobby, birthday, enrolldate);
list.add(m);
}
close(pstmt);
} catch (Exception e) {
e.printStackTrace();
}
return list; // 만일 실패해도 빈리스트가 나가는게 유리하다.
}
public int insertMember(Connection conn, Member member) {
try {
String sql = "INSERT INTO MEMBER VALUES(default, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, default)";
PreparedStatement pstmt = conn.prepareStatement(sql);
int count = 1;
pstmt.setString(count++, member.getID());
pstmt.setString(count++, member.getPassword());
pstmt.setString(count++, member.getNAME());
pstmt.setString(count++, member.getGENDER());
pstmt.setInt(count++, member.getAGE());
pstmt.setString(count++, member.getEMAIL());
pstmt.setString(count++, member.getPHONE());
pstmt.setString(count++, member.getADDRESS());
pstmt.setString(count++, member.getHOBBY());
pstmt.setDate(count++, new java.sql.Date(member.getBIRTHDAY().getTime()));
int result = pstmt.executeUpdate();
close(pstmt);
return result;
} catch (Exception e) {
e.printStackTrace();
}
return -1;
}
public int updateMember(Connection conn, Member member) {
try {
String sql = "UPDATE MEMBER SET PASSWORD = ?, EMAIL = ?, PHONE = ?, "
+ "ADDRESS = ?, HOBBY = ? WHERE ID = ?";
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setString(1, member.getPassword());
pstmt.setString(2, member.getEMAIL());
pstmt.setString(3, member.getPHONE());
pstmt.setString(4, member.getADDRESS());
pstmt.setString(5, member.getHOBBY());
pstmt.setString(6, member.getID());
int result = pstmt.executeUpdate();
close(pstmt);
return result;
} catch (Exception e) {
e.printStackTrace();
}
return -1;
}
public int deleteMember(Connection conn, String memberId) {
try {
String sql = "DELETE FROM MEMBER WHERE ID = ?";
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setString(1, memberId);
int result = pstmt.executeUpdate();
close(pstmt);
return result;
} catch (Exception e) {
e.printStackTrace();
}
return -1;
}
public static void main(String[] args) {
Connection conn = JDBCTemplate.openConnection();
MemberDao dao = new MemberDao();
List<Member> list = dao.selectAll(conn);
list.forEach((v)->System.out.println(v));
System.out.println("---------------------------------------------------");
Member m = dao.selectOne(conn, "user1");
System.out.println(m);
System.out.println("---------------------------------------------------");
m.setID("test12122");
int result = dao.insertMember(conn, m);
System.out.println("insert result : "+ result);
System.out.println("---------------------------------------------------");
list = dao.selectAll(conn);
list.forEach((v)->System.out.println(v));
System.out.println("---------------------------------------------------");
}
}
[MemberController.java]
package member.contoller;
import java.util.List;
import member.model.service.MemberService;
import member.model.vo.Member;
// Controller : Spring에서는 url을 통해 요청을 받고 서비스를 다중으로 보유할수 있는 객체
public class MemberController {
MemberService service = new MemberService();
// BoardService boardService = new BoardService();
// ShopService shopService = new ShopService();
public List<Member> getAllMemberList() {
return service.getAllMemberList();
}
public Member searchById(String memberId) {
return service.searchById(memberId);
}
public List<Member> searchByName(String memberName) {
return service.searchByName(memberName);
}
public int insertMember(Member member) {
return service.insertMember(member);
}
public int updateMember(Member member) {
return service.updateMember(member);
}
public int deleteMember(String memberId) {
return service.deleteMember(memberId);
}
}
[MemberService.java]
package member.model.service;
//import static : static 메소드를 자기 메소드처럼 활용할수 있는 기법
import static common.JDBCTemplate.*;
import java.sql.Connection;
import java.util.List;
import member.model.dao.MemberDao;
import member.model.vo.Member;
// model단 코드로 Member에 관련된 서비스를 담당하는 코드 (DAO를 소유하고 있는 객체)
// Service는 DAO를 여러개를 보유 할수도 있다.
public class MemberService {
/**
* Service의 역할
* 1. 특정 기능에 대한 모든 서비스를 담당하는 구조로 Table(DAO)가 다중일지라도 컨트롤러에서는 하나의 구조로 보이게끔 하는 역할
* ex) 다중 DAO 관리, Join문 or 다중쿼리 관리, 다중 트랜잭션 관리
*
* 2. DB 상태, 접속 관리
* - 생성, Connection 관리, 종료
*
* 예시1)
* 3개의 DAO에서 정보를 조회해 올 때 -> Service에서는 3개의 DAO로 부터 데이터를 받아와 하나의 객체로 반환함
* Member / Job / Hobby
* ID/이름/J1 J1/주임 ID/취미1, ID/취미2
* --> 컨트롤러로 전달 할 때는 MemberDTO에 Member, Job, Hobby에 대한 정보를 묶어서 전달함
*
* 예시2)
* DELETE 할때 총 2개의 Member, Hobby 테이블이 존재할때 두개의 DELETE문을 트랜잭션으로 묶어서 관리
* 모두 성공하면 commit, 만일 하나만 삭제되고 후단에서 error가 발생하는 경우 rollback 시킬수 있음
*/
private MemberDao memberDao = new MemberDao();
// private JobDao jobDao = new MemberDao(); // 예시
// private HobbyDao hobbyDao = new MemberDao();
private Connection conn;
public MemberService() {
conn = openConnection();
}
// 서비스 메소드를 정의
// selectAll(Connection)
// selectOne(Connection, String)
// selectByName(Connection, String)
// insertMember(Connection, Member)
// updateMember(Connection, Member)
// deleteMember(Connection, String)
public List<Member> getAllMemberList(){
return memberDao.selectAll(conn);
}
public Member searchById(String memberId) {
return memberDao.selectOne(conn, memberId);
}
public List<Member> searchByName(String name) {
return memberDao.selectByName(conn, name);
}
public int insertMember(Member member) {
int result = memberDao.insertMember(conn, member);
if(result > 0) {
commit(conn);
}else {
rollback(conn);
}
return result;
}
public int updateMember(Member member) {
int result = memberDao.updateMember(conn, member);
if(result > 0) {
commit(conn);
}else {
rollback(conn);
}
return result;
}
public int deleteMember(String memberId) {
int result = memberDao.deleteMember(conn, memberId);
if(result > 0) {
commit(conn);
}else {
rollback(conn);
}
return result;
}
}
[MemberMenu.java]
package member.view;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.List;
import java.util.Scanner;
import member.contoller.MemberController;
import member.model.vo.Member;
public class MemberMenu {
private MemberController memberController = new MemberController();
private Scanner sc = new Scanner(System.in);
public void mainMenu() {
String menu = "========== 회원 관리 프로그램 ==========\n"
+ "1.회원 전체조회\n"
+ "2.회원 아이디조회\n"
+ "3.회원 이름조회\n"
+ "4.회원 가입\n"
+ "5.회원 정보변경\n"
+ "6.회원 탈퇴\n"
+ "0.프로그램 끝내기\n"
+ "====================================\n"
+ "선택 : ";
while(true) {
System.out.print(menu);
int choice = sc.nextInt();
Member member = null;
int result = 0;
String msg = null;
List<Member> list = null;
String memberId = null;
String memberName = null;
switch(choice) {
case 1:
list = memberController.getAllMemberList();
displayMemberList(list);
break;
case 2:
memberId = inputMemberId();
member = memberController.searchById(memberId);
displayMember(member);
break;
case 3:
memberName = inputMemberName();
list = memberController.searchByName(memberName);
displayMemberList(list);
break;
case 4:
//1.신규회원정보 입력 -> Member객체
member = inputMember();
System.out.println(">>> 신규회원 확인 : " + member);
//2.controller에 회원가입 요청(메소드호출) -> int리턴(처리된 행의 개수)
result = memberController.insertMember(member);
//3.int에 따른 분기처리
msg = result > 0 ? "회원 가입 성공!" : "회원 가입 실패!";
printMsg(msg);
break;
case 5:
member = updateMember();
result = memberController.updateMember(member);
msg = result > 0 ? "회원 수정 성공!" : "회원 수정 실패!";
printMsg(msg);
break;
case 6:
memberId = inputMemberId();
result = memberController.deleteMember(memberId);
msg = result > 0 ? "회원 탈퇴 성공!" : "회원 탈퇴 실패!";
printMsg(msg);
break;
case 0:
System.out.print("정말로 끝내시겠습니까?(y/n) : ");
if(sc.next().charAt(0) == 'y')
return;//현재메소드(mainMenu)를 호출한 곳
break;
default:
System.out.println("잘못 입력하셨습니다.");
}
}
}
/**
* 회원정보변경 메소드
* @return
*/
private Member updateMember() {
Member m = new Member();
System.out.print("변경할 회원 아이디 : ");
m.setID(sc.next());
System.out.print("암호 : ");
m.setPassword(sc.next());
System.out.print("이메일 : ");
m.setEMAIL(sc.next());
System.out.print("전화번호(-빼고입력): ");
m.setPHONE(sc.next());
System.out.print("주소 : ");
sc.nextLine();
m.setADDRESS(sc.nextLine());
System.out.print("취미(/로 공백없이 나열): ");
m.setHOBBY(sc.next());
return m;
}
private String inputMemberName() {
System.out.print("조회할 이름 입력 : ");
String name = sc.next();
return name;
}
/**
* DB에서 조회한 1명의 회원 출력
* @param member
*/
private void displayMember(Member member) {
if(member == null) {
System.out.println(">>>> 조회된 회원이 없습니다.");
return;
}
System.out.println("----------------------------------------------------------------");
System.out.println(member);
System.out.println("----------------------------------------------------------------");
}
/**
* 조회할 회원아이디 입력
* @return
*/
private String inputMemberId() {
System.out.print("아이디 입력 : ");
return sc.next();
}
/**
* DB에서 조회된 회원객체 n개를 출력
* @param list
*/
private void displayMemberList(List<Member> list) {
if(list == null || list.isEmpty()) {
System.out.println(">>>> 조회된 행이 없습니다.");
return;
}
System.out.println("----------------------------------------------------------------");
for(Member m : list) {
System.out.println(m);
}
System.out.println("----------------------------------------------------------------");
}
/**
* msg 출력
* @param msg : 처리 결과
*/
private void printMsg(String msg) {
System.out.println(">>> 처리결과 : " + msg);
}
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
/**
* 신규회원 정보 입력
* @return
*/
private Member inputMember() {
System.out.println("새로운 회원정보를 입력하세요.");
Member member = new Member();
System.out.print("아이디 : ");
member.setID(sc.next());
System.out.print("이름 : ");
member.setNAME(sc.next());
System.out.print("비밀번호 : ");
member.setPassword(sc.next());
System.out.print("나이 : ");
member.setAGE(sc.nextInt());
System.out.print("성별(M/F) : ");//m, f
member.setGENDER(""+sc.next().toUpperCase().charAt(0));
System.out.print("이메일: ");
member.setEMAIL(sc.next());
System.out.print("전화번호(-빼고 입력) : ");
member.setPHONE(sc.next());
sc.nextLine();
System.out.print("주소 : ");
member.setADDRESS(sc.nextLine());
System.out.print("취미(,로 나열할것) : ");
member.setHOBBY(sc.nextLine());
System.out.print("생일(yyyy-mm-dd) : ");
String str = sc.nextLine();
try {
sdf.parse(str);
member.setBIRTHDAY(sdf.parse(str));
} catch (ParseException e) {}
return member;
}
}
[Run.java]
package run;
import member.view.MemberMenu;
public class Run {
public static void main(String[] args) {
new MemberMenu().mainMenu();
System.out.println("---- 프로그램 종료 ----");
}
}
'자바' 카테고리의 다른 글
[Java] 예외처리 (Exception) (0) | 2023.12.19 |
---|---|
제네릭 (0) | 2023.12.19 |
[Java] JDBC (Java DataBase Connectivity) (0) | 2023.11.29 |
[Java] I/O 입출력 (0) | 2023.11.23 |
[Java] Collections - Map (0) | 2023.11.23 |