Java 정리 46
21 Jan 2019
Reading time ~3 minutes
Java 정리 46 - 도시락 주문 프로그램(6), 데이터를 전달하는 방식
개발
- 핵심 기능 구현 완료..
-
완성된 클래스다이어그램
-
Thread로 서버측에서 클라이언트 주문을 읽기
- 서버측에서 DB내용을 계속 읽어들이면 변경 내용을 바로 JTable에 표현가능
- 다만 부하가 많이 발생
- Thread.sleep()을 사용하여 일정시간마다 갱신하는 방법을 사용가능
- 또는 소켓으로 데이터를 받으면 갱신하는 방법도 가능
- read()메소드는 무한루프에 있더라도 읽어들일 내용이 있을때만 동작
- 부하가 적다.
- 서버측에서 DB내용을 계속 읽어들이면 변경 내용을 바로 JTable에 표현가능
// LunchMainController, Runnable을 implements
@Override
public void run() {
// 30초마다 한번씩 조회를 수행
try {
while (true) {
searchOrder();
Thread.sleep(1000 * 30);
}
} catch (InterruptedException e) {
JOptionPane.showMessageDialog(lmv, "조회 중 문제가 발생");
e.printStackTrace();
}
}
...
@Override
public void mouseClicked(MouseEvent me) {
if (me.getSource() == lmv.getJtb()) { // 탭에서 클릭이 발생했을 때만
if (lmv.getJtb().getSelectedIndex() == 1) { // 두번째 탭(주문)에서 이벤트 발생
// 실시간으로 DB를 조회하여 주문현황을 갱신
if (threadOrdering == null) {
threadOrdering = new Thread(this);
threadOrdering.start();
}
}
}
...
- 클라이언트 주문 목록 조회를 프로시저로 구현
- Out Parameter로 커서를 전달할 땐 SYS_REFCURSOR 사용
-- procedure lunch_order_select.sql
CREATE OR REPLACE PROCEDURE lunch_order_select(
i_order_name IN VARCHAR2,
i_phone IN VARCHAR2,
cur_order_list OUT SYS_REFCURSOR
)
IS
BEGIN
OPEN cur_order_list FOR
SELECT l.lunch_name,
TO_CHAR(o.order_date, 'yyyy-mm-dd day hh24:mi:ss') order_date,
o.quan
FROM ordering o, lunch l
WHERE l.lunch_code = o.lunch_code
AND o.order_name=i_order_name AND o.phone=i_phone
ORDER BY o.order_date DESC;
END;
/
- 자바에서 프로시저를 사용할 땐 CallableStatement를 사용
// LunchClientDAO
...
public List<OrderListVO> selectOrder(OrderInfoVO oivo) throws SQLException {
List<OrderListVO> list = new ArrayList<OrderListVO>();
Connection con = null;
CallableStatement cstmt = null;
ResultSet rs = null;
try {
con = getConn();
cstmt = con.prepareCall(" { call lunch_order_select(?,?,?) }");
// in parameter : 외부 값을 프로시저 안으로 넣을 때
cstmt.setString(1, oivo.getOrderName());
cstmt.setString(2, oivo.getOrderTel());
// out parameter : 프로시저 내부 값을 외부에서 받을 때
cstmt.registerOutParameter(3, OracleTypes.CURSOR);
cstmt.execute();
rs = (ResultSet)cstmt.getObject(3);
OrderListVO olvo = null;
while(rs.next()) {
olvo = new OrderListVO(rs.getString("lunch_name"),
rs.getString("order_date"), rs.getInt("quan"));
System.out.println(olvo);
list.add(olvo);
}
} finally {
if (rs != null) { rs.close(); }
if (cstmt != null) { cstmt.close(); }
if (con != null) { con.close(); }
}
return list;
}
...
- 접두사, 접미사가 정해진 파일을 한정지을 땐 contains말고 startsWith나 endsWith를 사용한다!
private String[] lunchImageList() {
String[] fileList = null;
String path = "C:/Users/owner/youngRepositories/SSangYoung/dev/workspace/lunch_prj/src/kr/co/sist/lunch/user/img/";
File dir = new File(path);
// 큰 이미지(s_가 붙지 않은)만 배열에 넣으세요.
List<String> listFileName = new ArrayList<String>();
for(String f_name : dir.list()) {
if (!f_name.startsWith("s_")) { // contains사용하면 위험!!!
listFileName.add(f_name);
}
}
fileList = new String[listFileName.size()];
listFileName.toArray(fileList);
return fileList;
}
- 서버에 도시락 추가 시 클라이언트가 가지지 않은 이미지 파일을 전송하는 기능이 필요
- 도시락 프로그램은 2 Tier 구조
- DB 서버 - 서버
- DB 서버 - 클라이언트
- 메뉴를 추가하면 추가된 이미지가 서버에만 추가됨
- 클라이언트에서 서버에 이미지명 배열을 보내면 서버가 없는 파일을 판단 후 파일로 전달
- 클라이언트와 서버에 Tier가 추가되어 n Tier 구조가 된다.
- 웹에선 DB 서버- Web 서버 - Web 클라이언트 3 Tier 구조가 일반적
- DB서버와 Web 서버 사이에 Middleware가 들어가 n Tier 구조가 될 수 있음
데이터를 전달하는 방식
-
Server Push
-
서버가 모든 클라이언트에게 발생된 데이터를 보내주는 방식
- 클라이언트는 가만히 있어도 데이터가 갱신된다
- 단점은 서버가 모든 클라이언트를 관리해야 함
- 자원 소모가 심함(웹에선 불가능)
-
서버가 모든 클라이언트에게 발생된 데이터를 보내주는 방식
-
Client Pull
- 클라이언트가 서버로 데이터를 요청하여 변경된 데이터를 받는 방식
- 서버가 클라이언트를 관리할 필요가 없다.
- 도시락앱에선 이미지명을 담은 배열을 서버에 보내 Client Pull 수행
-
서버
-
LunchAdminMain
- 서버가 시작되자마자 서버가 가진 이미지파일목록을 static list변수에 저장하고 FileServer Thread를 실행시킨다.
-
FileServer
- FileServer는 접속한 클라이언트가 있으면 클라이언트가 보내온 이미지파일목록과 비교하여 없는 파일을 보내주는 Thread
-
LunchAdminMain
-
클라이언트 LunchClientController
- 클라이언트는 이벤트객체가 생성되면서 현재 이미지파일목록을 서버에 전송하고 없는 파일을 전달받는다.
-
서버
-
다시 정리
- 서버는 시작되면서 static list변수로 현재 가진 이미지파일목록을 저장
- 클라이언트는 시작하면 자신이 가진 이미지파일목록을 서버에 전달
- 서버는 클라이언트가 보낸 목록과 static변수와 비교, 없는 파일의 수를 클라이언트에게 전달
- 클라이언트가 가지지 않은 파일명과 파일조각(바이트배열)을 파일의 수만큼 반복해서 서버는 전송, 클라이언트는 전달 받는다.