2013년 8월 27일 화요일

web application 등장 배경 및 구동 원리와 tomcat설치 및 간단한 web app 프로그램 만들기.

1. web application 등장 배경

  1. 기존 네트워크 프로그램은 소켓과 멀티 쓰레드를 이용 개발자가 직접 파일입출력을 다          룸. 클라이언트에서 App Server에 요청 해당 데이터를 DB에서 출력 받음.
  2. 클라이언트의 데이터 수집 방법이 링크형태를 띄고 FTP 프로토콜 방식을 사용.데이터를
      얻어옴.
  3. 이를 개선하고자 HTTP 프로토콜 방식의 등장.

  *CGI란? 각각의 HTTP프로토콜 응답규칙에 맞추기위한 규칙.


 * FTP 프로토콜 방식의 특징
      - 항상 접속이 유지되어야 한다.
      - 연결에 제한이 있다(연결하는 사람의 정보)
      - 게임처럼 동시에 변하는 데이타값을 여러 클라이언트에게 보낼때 사용되는 방식
      - 리소스가 누적되어 여러 클라이언트의 동시접속이 제한된다.
      - 속도가 빠르다.
  * HTTP 프로토콜 방식의 특징
      - 데이터의 입력시 연결되고 서버로부터 데이터를 출력후 종료된다.
      - 속도가 느리다.
      - 다수의 클라이언트 데이터 처리가 가능하다.
      - 리소스 누적이 적다.
      - 데이터의 단순 참조용으로 필요시연결만 할때 효과적이다.

2. 발전
      - 초기 단숝 논문 과 같은 텍스트 문서 참고시 불편을 해소하고자 소위 그물 처럼
      - 데이터를 공유 하는 WEB 방식으로 발전 속도의 문제점이 하드 디스크의 발전으로
      - 해결되면서 본격적인 네트워크로 자리잡음.

*서블릿 콘테이너란? 기존의 web서버와

webApplication 의 기능 을 묶어 클라이언트로부터
                              요청을 받아 해당 프로토콜에 맞게 변경 하여 DB접근 자체적으로
                              소켓통신과 쓰레드를 이용 각각의 클라이언트에게 데이터를 출력.


3. 톰켓 설치 및 이클립스 연동


*www.aphach.org 에 접속




*스크롤을 쭉 내려보면 톰캣이 보인다. 클릭하자.





각각의 운영체제에 맞게 32비트 또는 64비트 선택  serviceinstaller 를 받으면 안된다.



이클립스와 workspace가 있는 폴에더 설치.

*설치가 다됐으면 이클립스로 가서 WINDOW/PREPARENCES 선택



Runtime envirement  선택.


해당 톰켓 버전을 클릭 하여ㅑ 넥스트




디렉토리설정을 설치된 폴더에서 bin 폴더까지 보이면 확인.


피니쉬~~~




하단의 파란 글씨를 클릭하면 다이나믹 서버 프로젝트를 추가할껀지 묻지만 아직 없으니 피니쉬.




생성된 서버를 더를 클릭하면 설정을 변경할수 있다 http 1.1 에 포트번호를 변경~

start 버튼을 누르면 서버가 가동된다.



일단 서버가 정상적으로 구동되었는지를 확인.

new > Dynamic web Project 선택 

프로젝트이름을 설정하고 넥스트

또 넥스트





xml 파일을 자동으로 생성해주는 문구 체크 하고 피니쉬.




2013년 8월 19일 월요일

이클립스 mysql 연동 데이터 읽고 쓰기.

package net.bitacademy.java41.step10;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Date;
import java.util.Scanner;

/* Statememt vs. PreparedStatement
 * 1) 소스 가독성:
 * SQL문이 복잡할 수록 읽기 어렵다  <==> 간결하다.
 *
 * 2) 바이너리 데이터(이미지, 음악, 동영상 등) 입력:
 * 불가(base64와 같은 문자열 형식으로 인코딩 시 가능) <==> 가능
 *
 * 3) 반복 수행 시 속도:
 * 매번 SQL문을 DBMS 형식에 따라 변환 속도 느리다
 * <==> 한번만 변환, 그리고 실행, 속도 빠르다.
 */
public class RedApp {
protected Scanner scanner;
private Member member;

public RedApp() {
scanner = new Scanner(System.in);
}

public void execute() {
while(true) {
System.out.print("명령>");
String command = scanner.nextLine().toLowerCase();

if (command.equals("add")) {
member = null;
add();
} else if (command.equals("list")) {
member = null;
list();
} else if (command.startsWith("view")) {
//명령>view a@test.com
String[] values = command.split(" ");
view( values[1] );
} else if (command.equals("update")) {
if (member != null) {
update();
} else {
System.out.println("먼저 멤버를 조회하세요!");
}
} else if (command.equals("delete")) {
if (member != null) {
detele();
} else {
System.out.println("먼저 멤버를 조회하세요!");
}
} else if (command.equals("quit")) {
System.out.println("안녕!");
break;
} else {
System.out.println("지원하지 않는 명령어입니다.");
}
}
}

private void add(){
Member m = new Member();
System.out.print("이름 : ");
m.setName(scanner.nextLine());
System.out.print("전화번호 : ");
m.setPhone(scanner.nextLine());
System.out.print("이메일 : ");
m.setEmail(scanner.nextLine());
System.out.print("블로그 : ");
m.setBlog(scanner.nextLine());
System.out.print("나이 : ");
m.setAge( Integer.parseInt( scanner.nextLine() ) );

System.out.print("등록하시겠습니까?(y/n)");
if(scanner.nextLine().toLowerCase().equals("y")){
m.setRegDate(new Date());

Connection con = null; //등록은  sql문을 전달만하고  DB에서 기능이 이루어지기때문에 커넥션과 스테이트먼트만 준비
PreparedStatement stmt = null;

try {
String driverClass = System.getProperty("driverClass"); //커넥션 인터페이스를 구현하는 드라이버클래스 정보 를 담는다.
Class.forName(driverClass); //담은 클래스정보를 로딩

con = DriverManager.getConnection( //매니저를 이용 커넥션 인터페이스를 구현한 클래스들 중에서
"jdbc:mysql://localhost/test", "test", "test"); //즉 my sql과 연결된 모든 정보들중 해당 주소와 아이디 비번 값을 가진
// 테이블에 연결
stmt = con.prepareStatement( // sql문 보내기
"insert into MEMBERS(MNAME,PHONE,EMAIL,BLOG,AGE,REG_DATE)"
+ " values(?,?,?,?,?,now())"); // insert 로 칼럼 만들고 값에다가 ? now()는 현재 시간
stmt.setString(1, m.getName()); // 덱스 번호 순서에따라 입력받은 데이터를 얻어와서 입력
stmt.setString(2, m.getPhone()); //
stmt.setString(3, m.getEmail()); //
stmt.setString(4, m.getBlog());
stmt.setInt(5, m.getAge());
int count = stmt.executeUpdate(); //

System.out.println("등록되었습니다!");
} catch (Exception e) {
e.printStackTrace();
} finally {
try {stmt.close();} catch(Exception e) {}
try {con.close();} catch(Exception e) {}
}
} else {
System.out.println("등록 취소하였습니다!");
}
}

private void list() {
Connection con = null; //java.sql에 정의된 인터페이스 를 선언
Statement stmt = null; //서버에 sql문을 보낼 도구
ResultSet rs = null; //DB에 저장된 데이터를 가져오기 위한도구 위한 도구.

try {
// MySQL 서버에 연결할 도구를 준비
// - JDBC Driver 클래스 중(.jar 파일에 들어있는 클래스들 중에서)
//   java.sql.Driver 인터페이스를 구현한 클래스를 먼저 로딩한다.
// - 이 클래스가 java.sql.Connection 인터페이스를 구현한 클래스를 알고 있다.
//
String driverClass = System.getProperty("driverClass"); // 시스템 클래스의 getProperty 메소드를 이용 driverclass의 정보를 얻어와
Class.forName(driverClass); // String 타입으로 반환한다. 이것을 class.forName 메소드를 이용 해당클래스들을
//Class.forName("com.mysql.jdbc.Driver"); // 로딩한다.

// 1. 서버와 연결
con = DriverManager.getConnection( //로딩된 클래스들중 mysql인터페이스를 구현하는 클래스정보를
"jdbc:mysql://localhost/test", "test", "test"); //해당 주소,아이디,비밀번호 가진 mysql 데이터와 연결

// 2. SQL문을 보낼 도구를 얻기
// - createStatement()를 호출하여 SQL문을 서버에 보낼 도구를 얻는다.
stmt = con.createStatement();

// 3. SQL을 서버에 보냄
// - executeQuery()는 서버에 SQL문을 보낸다.
// - 서버는 결과를 준비한다.
// - executeQuery()는 서버에서 결과를 가져올 도구를 리턴한다.
rs = stmt.executeQuery(
"select MNAME,PHONE,EMAIL from MEMBERS order by MNAME"); // order by 는 이름순으로 정렬해서

// 4. 서버의 결과를 가져와서 출력
// - next()를 호출하여 서버로부터 1 레코드의 결과를 가져온다.
while(rs.next()) {
System.out.print(rs.getString("MNAME") + ","); //보내기위한 도구 rs에서 결과를 가져오는 메소드 next 호출
System.out.print(rs.getString("PHONE") + ","); //읽어올 데이타가 없을경우 1을 반환 false를 반환
System.out.println(rs.getString("EMAIL")); //문자열로 반환 화면에 출력
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {rs.close();} catch (Exception e) {} //마찬가지로 연결도구 모두 종료.
try {stmt.close();} catch (Exception e) {}
try {con.close();} catch (Exception e) {}
}
}

private void detele() {
Connection con = null; //연결도구
Statement stmt = null; //보낼도구

try {
String driverClass = System.getProperty("driverClass"); //역시 마찬가지로 연결을 위한 인터페이스를 구현하는 클래스를 로딩
Class.forName(driverClass);

con = DriverManager.getConnection( //로딩된 클래스에 manager 를 사용 해당주소로 연결
"jdbc:mysql://localhost/test", "test", "test");

stmt = con.createStatement(); //보낼도구 준비

int count = stmt.executeUpdate( // excuteUpdate 메소드를 이용 sql문을 이용 보냄.
"delete from MEMBERS"
+ " where EMAIL='" + member.getEmail() + "'"); // view 에서 이메일을 입력했을 경우 인스턴스변수 m 은 null 이 아니게되고.
// excuteUpdate 는 조건에 맞는 칼럼을 삭제하고 칼럼수를 인트로 반환
if (count > 0) { // 0보다 큰경우는 삭제를 확인
System.out.println("삭제되었습니다!");
} else {
System.out.println("해당 이메일의 멤버를 찾을 수 없습니다!"); // 그렇지 않은 경우는 해당 메세지 출력
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {stmt.close();} catch(Exception e) {}
try {con.close();} catch(Exception e) {}
}
}

private void update() {
Member copy = member.clone();
System.out.printf("이메일(%1$s):\n", member.getEmail()); // 이메일은 키(아이디)값이기 때문에 변경을 하지 않는다.
System.out.printf("이름(%1$s):", member.getName());
String value = scanner.nextLine();
if (value.length() > 0) {
copy.setName(value);
}
System.out.printf("전화(%1$s):", member.getPhone());
value = scanner.nextLine();
if (value.length() > 0) {
copy.setPhone(value);
}
System.out.printf("블로그(%1$s):", member.getBlog());
value = scanner.nextLine();
if (value.length() > 0) {
copy.setBlog(value);
}
System.out.printf("나이(%1$d):", member.getAge());
value = scanner.nextLine();
if (value.length() > 0) {
copy.setAge( Integer.parseInt(value) );
}

System.out.print("변경하시겠습니까?(y/n)");
if(scanner.nextLine().toLowerCase().equals("y")){
Connection con = null;
PreparedStatement stmt = null;

try {
String driverClass = System.getProperty("driverClass"); //역시 해당드라이버들 로딩
Class.forName(driverClass); //로딩

con = DriverManager.getConnection( // 해당 주소 연결
"jdbc:mysql://localhost/test", "test", "test");

stmt = con.prepareStatement( //sql문 전달.
"update MEMBERS set"
+ " MNAME=?,PHONE=?,BLOG=?,AGE=?,REG_DATE=now()" // ? 지역에 setString 을 이용 인덱스번호대로 입력한다.
+ " where EMAIL=?");
stmt.setString(1, copy.getName());
stmt.setString(2, copy.getPhone());
stmt.setString(3, copy.getBlog());
stmt.setInt(4, copy.getAge());
stmt.setString(5, member.getEmail());
int count = stmt.executeUpdate(); //역시 count로 반환

if (count > 0) {
System.out.println("변경되었습니다!"); //해당 데이터 변경
} else {
System.out.println("해당 이메일의 멤버를 찾을 수 없습니다."); //조건에 맞는 해당 데이터가 없을경우 count 값은 -1이기 때문에.
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {stmt.close();} catch(Exception e) {}
try {con.close();} catch(Exception e) {}
}
} else {
System.out.println("변경 취소하였습니다!");
}
}

private void view(String email) {
Connection con = null; //view 도 역시 list와 같이 데이터를 불러와야하기 때문에 ResultSet을 준비 한다.
Statement stmt = null;
ResultSet rs = null;

try {
String driverClass = System.getProperty("driverClass"); //해당 인터페이스를 구현하는 클래스 정보를 담는다
Class.forName(driverClass);       //클래스 로딩

con = DriverManager.getConnection(
"jdbc:mysql://localhost/test", "test", "test"); //연결

stmt = con.createStatement(); //sql문 전달.

rs = stmt.executeQuery( // 해당 칼럼 선택.
"select MNAME,PHONE,EMAIL,BLOG,AGE,REG_DATE"
+ " from MEMBERS"
+ " where EMAIL='" + email + "'"); // 조건은 입력받은 이메일 주소와 같은 이메일 주소데이타.

if (rs.next()) { //새로운 멤머 객체를 만들고 set 메소드를 이용 그안에 파라미터로
member = new Member(); //읽기위한 도구 rs(ResultSet)으로 getString 메소드 호출 파라미터로
member.setName(rs.getString("MNAME")); //칼럼이름.
member.setPhone(rs.getString("PHONE"));
member.setEmail(rs.getString("EMAIL")); //즉 입력된 칼럼이름 안에 데이터를 문자열 타입으로 변환 Member 인스턴스에 생성한다.
member.setBlog(rs.getString("BLOG"));
member.setAge(rs.getInt("AGE"));
member.setRegDate(rs.getDate("REG_DATE"));

} else {
throw new Exception("해당 이메일의 멤버가 없습니다."); //
}
} catch (Exception e) {
e.printStackTrace();
return;
} finally {
try {rs.close();} catch (Exception e) {}
try {stmt.close();} catch (Exception e) {}
try {con.close();} catch (Exception e) {}
}

System.out.println("이름:" + member.getName()); //셋팅된 Member 객체에서 출력.
System.out.println("전화:" + member.getPhone());
System.out.println("이메일:" + member.getEmail());
System.out.println("블로그:" + member.getBlog());
System.out.println("나이:" + member.getAge());
System.out.printf("등록일:%1$tY-%1$tm-%1$td \n", member.getRegDate());
}

public static void main(String[] args) {
RedApp app = new RedApp();
app.execute();
}
}








2013년 8월 14일 수요일

Mysql기초 연산자 활용

어젠!!!
테이블을생성 하고 데이터베이스에 값을 넣는 방법을 알아보았다.

오늘은 생성 된 테이블의 값을 변경 특정 조건에 따른 호출 과 삭제에 대해서 알아보겟다

여러가지 정의된 연산자가 많지만 일단 배운것 위주로 기초적인것만 적어보겟다.

1.desc 테이블명
 - 테이블에 대한 정보를 출력

desc MEMBERS;

2.데이터 조회
- select 컬럼명, 컬럼명, ...
  from 테이블명
- select *
  from 테이블명

select *
from MEMBERS;




* 조건 조회
select *
from 테이블명
where
컬럼명 연산자 값 and/or 컬럼명 연산자 값 ....
*/
select *
from MEMBERS
where
MNAME='임꺽정' and PHONE='111-1113';      두가지 조건을 다 만족할경우 호출한다.

select *
from MEMBERS
where AGE > 30;                                                   AGE 가 30보다 클경우 호출.

select *
from MEMBERS
where
AGE > 30 or MNAME='임꺽정';                AGE 가 30보다 크거나 MNAME 이 임꺽정이
                                                                    면 호출

/* 연산자: <, >, <=, >=, =, <> */
select *
from MEMBERS
where AGE <> 40 or AGE is null;                      AGE값이 40이 아니거나 NULL이면 호출

select *
from MEMBERS
where AGE <= 30;                                           AGE 값이 30보다 작거나같으면 호출

select *
from MEMBERS
where MNAME > '일지마';                               MNAME이 일지마보다 같은 초성중성을
                                                                     갖고 그이상 획수가 많을때 호출

select *
from MEMBERS            
where REG_DATE > '2013-08-11';                    RFG_DATE 2013 8월11일 이후면 호출

select *
from MEMBERS
where REG_DATE >= '2013-08-12' and             REG_DATE 가 8월 12일보다 이후이고
REG_DATE < '2013-08-15';                      15일 이전일때호출

/* like */
select *
from MEMBERS
where MNAME like '임%';                                MNAME이 '임'으로 시작할때 호출

select *
from MEMBERS
where MNAME like '%꺽정';                             MNAME 이 '꺽정' 으로 끝날때 호출

select *
from MEMBERS
where MNAME like '%정%';                             MNAME 의 문장에 '정'이 가운데 위치하면
                                                                    호출
select *
from MEMBERS
where MNAME like '임_정';                              MNAME 양끝에 각각'임''정'이위치하면호출

/* between a and b : x >= a and x <= b */      
select *
from MEMBERS
where AGE >= 20 and AGE <= 30;                    AGE가 20보다 크거나같고 30보다 작거나
                                                                    같으면 호출

select *
from MEMBERS                                             AGE가 20과 30사이면 호출
where AGE between 20 and 30;                          

/* in (값, 값, 값) */
select *
from MEMBERS
where AGE in (20, 35, 40);                              AGE,의 값이 20,35,40이면 호출

select *
from MEMBERS
where AGE not in (20, 35, 40);                        AGE,의 값이 20,35,40이 아니면 호출

/* not */
select *
from MEMBERS
where not MNAME='임꺽정';                          MNAME이 '임꺽정'이 아니면 호출

select *
from MEMBERS
where MNAME<>'임꺽정';                             MNAME이 '임꺽정'과 다르면 호출

select *
from MEMBERS
where EMAIL='leem@test.com';                     EMAIL이 'leemtest@.com'이면 호출

select MNAME,PHONE,EMAIL
from MEMBERS;                               MEMBERS 의 컬럼중 MNAME,PHONE,EMAIL 호출

/* 데이터 변경
update 테이블명 set 컬럼명=값, 컬럼명=값, ...
where 조건
*/
update MEMBERS set REG_DATE='2013-08-10'
where EMAIL='leem@test.com';

update MEMBERS set REG_DATE='2013-08-11'
where EMAIL='hong@test.com';

update MEMBERS set REG_DATE='2013-08-12'
where EMAIL='ill@test.com';

update MEMBERS set REG_DATE='2013-08-13'
where EMAIL='kang@test.com';

update MEMBERS set
MNAME='홍길동2',
PHONE='112-1111',
BLOG='hong.blog.com',
AGE=20,
REG_DATE=now()
where
EMAIL='hong@test.com';




/* 데이터 삭제
delete from 테이블명
where 조건
*/
SET SQL_SAFE_UPDATES=0;

delete from MEMBERS
where MNAME='일지매';

select *
from MEMBERS;

delete from MEMBERS
where PHONE like '%1111' or PHONE like '%1112';

delete from MEMBERS
where EMAIL='hong@test.com';












       

2013년 8월 13일 화요일

mysql를 이용 table 생성 하고 데이터 저장하기

sql 작성법은 크게 세가지가 있다.

1. DDL : Data를 저장할 틀을 정의 -> 테이블 생성/삭제/변경

2. DML : Data 를 입력 삭제 변경

3. DQL : data를 조회

기타...

일단 테이블을 생성 하고 데이터를 입력하는 기능의 명령어 와 방법에 대해 알아보겟다.

1. 일단 Mysql workbench 를 실행하면 아래와 같은 화면이 나올것이다. 이때  왼쪽에
sql development 에 미리 생성해두었던 test 로 로그인한다. 비밀번호 필요.
오른 쪽 소스 작성란에 코딩 하게되는게 
일단 기본적인 테이블을 생성 하고 데이터를 저장 하는 명령어에 대해 알아보자.

  - create table 테이블이름 (); = 테이블 생생을 하는 명령어로 ()에 명령을 수행한다.
     역시 세미콜론으로 명력어의 마지막을 표시하고  ()안에 는 저장할 변수를 크기를 정하고
     선언한다

  -  변수이름 varchar(크기) not null;
      과 같이 선언해준다. varcahr 는 문자열 데이터를 의미 varbinary 처럼 문자열 형식의
      데이터 타입이 여러가지 존재하지만 상황에 따라 쓰임새가 다르니 일단 한글문자열
      형식인 varchar 를 익숙히 하자. 크기는 숫자 1당 1바이트를 의미한다 참고로 한글은
      바이트로 따지면 최대 3바이트니 참고하자. not null은 데이타를 저장할 공간을 만들었
      으니 반드시 저장해야한다는 의미과 같다. 
   

  -  drop table
      mysql 에선 프로그램 자체는 영대소문자를 구분하지 않지만. 프로그래머 사이들에서
      정해진 규칙인 변수와 클래스 정보에관해선 대문자로 표현하도록한다.
      drop table MEMBERS; 이와같이 선언하면 MEMBERS 테이블을 모두 끝내는 명령어로
      저장하기위해 만들어놓은 데이타 저장공간 자체가 없어진다.

  -   insert into
      insert into 테이블이름(변수이름)
      values ('값');
      위 명령어는 테이블내에 선언된 변수에 값을 저장한다는 뜻이다.
예)
테이블생성과 함께 변수를 선언 크기를 정하고
선언된 변수에 값에 직접 데이타를 써서 왼쪽 상단에 번개표시클릭

왼쪽 test\Table\members 마우스오른쪽 버튼 에디트 실행하면 변수가 저장됨을 
확인할수 있다.
add primary key 라는 키워든 나중에 예제를 들어 다시 설명하겟다.

Data Base Management System (DBMS) 의 목적과 구동 원리

1. 목적
 파일 입출력에 관한 개발자의 편의성을 위한 자동기능을 제공한다.
 - 데이타 저장 조회 변경 삭제 관리전문 S/W
 - 효율적인 데이타 관리(중복제거)
 - data 결함제거
2. 구동방법
 - 여러가지 DBMS 에 따른 API 를 native AIP로 구현해놓았기때문에 (.dll파일) app에서
 - 해당 api호출 DBMS 프로토콜에 맞추어 서버에 전달 하여 파읽을 읽거나 쓰고 서버에서
 - 보낸 결과를 다시 app에 리턴하는 형태를 가지고 있다. 
 - 위와 같은 NetWork  Programing 에서 는 ID/pw , ip, 프로토콜 처럼 데이타를 보내는  
 - 규칙이 있는데 각 DBMS 의 회사마다 다르다. 따라서 반드시 DBMS 회사의 특정 프로
 - 그램을 설치해주어야만 오류가 발생하지 않는다.

3.개방형데이타베이스 연결
 - 위와 같은 형태의 문제점 즉 개발자의 코딩양이 늘어나느것에 대한 문제점을 해결하기
 - 위해 개방형 네트워크 방식을 MS사에서 도입했는데 즉 각각 DBMS 의 API를 최대한
 - 동일하게 하여 호환성을 최대화 하는 방식을 수용하고있다. 
 - API가의 정의 ODBC  드라이버엔 모든 DBMS의 API가 있는것은아니기때문에 때에따라
 - API 를 재정의 해야할 필요성이 있다.
4. driver type
 - 타입에 따라 파일을 입출력하는 방법이 조금 다른데 각각 특징을 살펴보겟다.
   -type .1 
       - ODBC Driver 를사용
       - JRE에 포함되어있다.
       - 속도가 느리다.
       - Excel Access 와 같은 파일기반 DBMS 를 사용할수 있다.
   - type.2
       - 각각의 DBMS 회사로부터 다운로드
       - Native Api 를 사용
   - type.3
       - 거의 사용하지 않는 타입이기때문에 생략
   - type.4
       - 현재 실무에서 제일 많이 사용됨
       - 직접 DBMS 와 연결
       - 순수한 자바언어로 이루어져있음


일반적으로 실무에서는 oracle 을 많이 사용하지만 window 8 에서 호환이 힘들고 설치가
까다롭기때문에 일단 oracle mysql을 설치 사용해보록 하겟다.

2013년 8월 12일 월요일

java IO관련 Serializable 인터페이스 와 입출력 방법



지난시간에 DataInputStream 클래스의 기능 을 이용 직접 데이타를 파일에 저장하는 연습을

해보았다. 하지만 루프를 이용한다해도 매번 해당 객체에 인스턴스를 호출 하여 값을

초기화하고 그값을 다시 인스턴스를 호출 하여 파일을 읽고 쓰는 방법은 인스턴스 변수

의 갯수가 많지않고 저장한파일의 원본 데이터를 외부에서 그대로 가져다 쓰기위함이 아니

라면,,, 특히 인스턴스 변수가 1000개라면... 아마도 쓸때없는 코드의 양이 엄청나게 늘어날

것이다 .

따라서 java에서 IO패키지를 살펴보면 객체 가 가지고 있는 내부적인 인스턴스를 자동으로

호출 하여 읽고 쓸수 있는 클래스가 있는데 ObjectOutputStream/ObjectInputStream 이다

말 그대로 객체를 읽고 쓰는 기능의 클래스이다. DataInputStream 클래스처럼 

FileInputStream 과  FileOutputStream 객체를 parameter 로 전달받고 기능을 확장 



전달받은 객체의 인스턴스에 접근하여 코드 를  바이트에배열로 전환 하여 데이타를 읽고 

쓸수있다. 이때 유의할점은 실체 데이터를 읽고 쓰는 것은 ObjectInput/OutputStream 객체

가 아니라 단지 FileInputStream/FileOutputStream 의 바이트 단위로 읽고 쓰는 기능을 

바이트에 배열로 바꾸어주는 역할만 하는것이므로 실제로 읽고 쓰는 기능 은 

FileInputStream 과 FIleOutputStream 인 것이다.


스트림 순서를 나열해보겟다.

1. 클래스 정보 로딩 new  연산자를 통한 객체 생성

2. 저장할 파일명과 파일을 쓸 파일명을 FileOunputStream 에 파라미터로 전달

3. FileOutputStream 객체를 ObjectOutputStream 객체생성하고 parameter 로 전달

4. ObjectOutputStream 객체에서 바이트의 배열로 파일을 쓰는 메소드 writeObject()호출

5.  writeObject() 에 인자로 쓸 파일을 전달한다.

6. 처음에 FileOutputStream 객체로 전달된 파일명을 가진 파일이 생성되고 그안에

    5번에서 전달된 객체의 인스턴스변수값이 바이트의 배열로 전달되어 생성된다.
7. FileInputStrea 객체생성하고 읽어드릴 파일명을 파라미터로 전달
8. ObjectOutputStream 객체 생성하고 파라미터로 FileInputStream 객체 전달(기능확장)
9. 읽어드리는 정보를 받을 변수를 선언 ObjectInputStream의 객체에서 readObject()호출
   (이때 읽어드리는 타입이 Object 이므로 해당 객체타입으로 다운캐스팅을 해주어야한다.
데코레이트 패턴
위 그림과 파일을 쓰는 순서가 달라보일수도 있지만 중요한것을 기억하자

실제 파일을 읽고쓰는 객체는 FileInput/OutputStream 인것을 .

사실 위 그림을 실제 코딩하게 되면 문제가 생긴다. 기본적으로 파일의 보안성을 위해

java 에서는 파일에 데이터를 읽고 쓰기위해선 어떤 조건을 만족해야하는데...

그것이 바로 Serializable interface 이다.

먼저 interface 에 간단하게 설명 하자면 프로그램을 하는 있어서 어떠한 스펙을 부여하여

제한을 두는것이라 생각할수 있겠다. 예를 들자면 게임을 해야하는데 마우스로 입력을 받는

다는 제한을 두면 마우스입력이라는 기능을 가진 플레이어(객체만) 게임을 할수 있는거와

비슷하다고 생각할수있다. 위 ObjectOutputStraem와 ObjectInputStrea을 이용해

byte 배열로 파일을 외부및 데이타 베이스에 읽고쓰기위해선  Serializable 인터페이스를

반드시 구현해주어야하는데 키워드는

      public static void  Hello implements 인터페이스이름 {}

위와같은 방법으로 구현한다. 경우에따라 반드시 오버라이드 해줘야하는 메소드가 존재할때

도 있고 그렇지 않을때도 있다. 인테페이스에 대해선 나중에 상속과 더불어 자세히 기술하겟

다.

2013년 8월 9일 금요일

RedApp 클래스기능 추가(IO패키지 클래스를 이용 파일에 저장하고 읽기)

package net.bitacademy.java41.step08;

import java.io.BufferedReader;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Scanner;

public class RedApp {
protected Scanner scanner;
protected ArrayList<Member> list = new ArrayList<Member>();
private int no;

// * 생성자
// - 인스턴스가 사용되기 전에 역할을 수행하는데 필요한 
//   최소한의 것을 준비하는 메서드
// * default constructor
// - 파라미터가 없는 생성자.
// - 클래스에 명시적으로 선언된 생성자가 없다면,
//   컴파일러가 자동으로 기본 생성자를 추가한다.
// public RedApp() {}

public RedApp() {
scanner = new Scanner(System.in);
}

public void execute() {
while(true) {
System.out.print("명령>");
String command = scanner.nextLine().toLowerCase();

if (command.equals("add")) {
no = 0;
add();
} else if (command.equals("list")) {
no = 0;
list();
} else if (command.startsWith("view")) {
//명령>view 2
String[] values = command.split(" ");
no = Integer.parseInt(values[1]);
view( no );
} else if (command.equals("update")) {
if (no > 0) {
update();
} else {
System.out.println("먼저 멤버를 조회하세요!");
}
} else if (command.equals("delete")) {
if (no > 0) {
detele();
} else {
System.out.println("먼저 멤버를 조회하세요!");
}
} else if (command.equals("quit")) {
System.out.println("안녕!");
break;
} else if (command.startsWith("save")) { //sava 메소드를 호출하고
String[] values = command.split(" ");//파일을 생성한다.
String filename = "default.data"; //공백뒤 문자를 String타입으로저장
if (values.length > 1) { //저장할 파일을 지정한 경우,
filename = values[1]; //배열의 첫번째 즉, 파일명을 변수에 담                                                                                  는다. 입력된 파일은 해당 확장자로                                                                                 자동생성
}

save(filename); //세이브 메소드 호출과 String 변수 전달

}  else if (command.startsWith("open")) { //open 메소드 호출 파일명 입                                                                                          력받음
String[] values = command.split(" "); //공백뒤에 오는 파일명을 저장
String filename = "default.data"; //단순 초기화문
if (values.length > 1) { //저장할 파일을 지정한 경우,
filename = values[1];  // 파일명을 변수에 담는다 해당 파일이                                                                               없다면 읽어드릴 테이터가 없기때문에                                                                              Exception이 발생할수있다.
}

open(filename); //open 메소드 호출하고 파일명을 파라미터로 전달

} else {
System.out.println("지원하지 않는 명령어입니다.");
}
}
}

private void open(String filename) {//open 에서 날라온 String 타입 파일명
FileReader in = null;
BufferedReader in2 = null;
// * BufferedReader 데코레이터
// - 버퍼 기능. 한 번 읽을 때 8192바이트를 읽어서 char[] 배열에 저장해둔다.
// - 읽을 때는 버퍼에서 읽는다. 버퍼를 모두 읽으면 다시 버퍼를 채운다. 
// - 라인 단위로 읽어서 리턴하는 메서드가 있다.
try {  // 예외 발생지역이므로 try  catch로 처리
in = new FileReader(filename); //파읽을 읽어드리는 위치를 전달받은
                                                                      파일명으로 전달
in2 = new BufferedReader(in);  // 8kbyte 크기의 배열로 파일을 읽기위한                                                                           객체
String line = null;
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");// Date객체                                                                                                    를 얻어오기위해 생성

list.clear();//현재 list에 저장된 M객체들을 모두 삭제
while(true) {
line = in2.readLine();//지정한 파일에 데이터를
                                                            라인별로 읽어드리는 메소드
if (line == null)     //더이상 읽을 데이터가없을시 null 값을 반환
break; // 조건문을 통해 빠져나감
// 따라서 총 저장된 파일의 라인수 만큼                                                                                루프를 돈다.
String[] values = line.split(","); // 읽은 파일을 String 배열에
list.add(                          // ,를 기준으로 나누어 담는다.
new Member()                
.setName(values[0])      //나눠진 값을 각각의 인스턴스                                                                                          변수에 초기화 한후
.setPhone(values[1]) // 그 객체정보를 리스트에 담는다.
.setEmail(values[2])
.setBlog(values[3])
.setAge( Integer.parseInt(values[4]))
.setRegDate(df.parse(values[5])) //심플데이타 포맷 객                                                                                                체 df에서 parse메소드를                                                                                                호출 하고 읽어드린 날짜                                                                                                문자라인을 yyyy-MM-dd
);      //타입으로 표기하는 Date                                                                                                        객체를 생성한다.                                                                                                 그리고 set메소드를 이용                                                                                                         다시 초기화한다.
}
System.out.println("데이터 로딩이 완료되었습니다!");
} catch (Exception e) { //예외처리
System.out.println("파일로딩 중에 오류가 발생했습니다!");
} finally {
try {in2.close();} catch(Exception e) {} //예외 발생여부에 상관없이                                                                                                   FileReader 객체와                                                                                                  BerfferdReader 객체를 닫아준다.
try {in.close();} catch(Exception e) {}  // 연결을 해제.
}
}

private void save(String filename) { //역시 저장할 파일명을 전달받는다.
                                                              자동으로 파일이 생성됨.
FileWriter out = null;// 데이터 타입이 문자이므로 Reader를 준비해야한다.
PrintWriter out2 = null;// BerfferdReader 로 읽어드린것을 다시 쓸때 연결해야할                                                      파이프
try {
out = new FileWriter(filename); //이부분에서 자동으로 전달된 파일이 생성                                                                        된다.
out2 = new PrintWriter(out); //해당파일에 쓸 파이프를 연결한다. 단 기능을                                                                        확장

for(Member m : list) {
out2.printf(
"%1$s,%2$s,%3$s,%4$s,%5$d,%6$tY-%6$tm-%6$td\n",
m.getName(), m.getPhone(), m.getEmail(), m.getBlog(),
m.getAge(), m.getRegDate());
}

System.out.println("저장되었습니다!");
} catch (Exception e) {
System.out.println("파일저장 중에 오류가 발생했습니다!");
} finally {
if (out2 != null)
out2.close();
try {out.close();} catch(Exception e) {}
}
}

private void add(){
Member m = new Member();
System.out.print("이름 : ");
m.setName(scanner.nextLine());
System.out.print("전화번호 : ");
m.setPhone(scanner.nextLine());
System.out.print("이메일 : ");
m.setEmail(scanner.nextLine());
System.out.print("블로그 : ");
m.setBlog(scanner.nextLine());
System.out.print("나이 : ");
m.setAge( Integer.parseInt( scanner.nextLine() ) );

System.out.print("등록하시겠습니까?(y/n)");
if(scanner.nextLine().toLowerCase().equals("y")){
m.setRegDate(new Date());
list.add(m);
System.out.println("등록되었습니다!");
} else {
System.out.println("등록 취소하였습니다!");
}
}

private void list() {
System.out.printf("%1$3s %2$10s %3$15s %4$30s\n",
"번호", "이름", "전화", "이메일");
/*
for(int i = 0; i < list.size(); i++) {
Member m = list.get(i);
}
*/
// 위의 for문 보다 간결한 표현식
int no = 1;
for(Member m : list) {
System.out.printf("%1$3d %2$10s %3$15s %4$30s\n",
no++, m.getName(), m.getPhone(), m.getEmail());
}
}

private void detele() {
list.remove(no - 1);
no = 0;
System.out.println("삭제되었습니다!");
}

private void update() {
Member m = list.get(no - 1);
Member copy = m.clone();
System.out.printf("번호(%1$d):", no);
System.out.printf("이름(%1$s):", m.getName());
String value = scanner.nextLine();
if (value.length() > 0) {
copy.setName(value);
}
System.out.printf("전화(%1$s):", m.getPhone());
value = scanner.nextLine();
if (value.length() > 0) {
copy.setPhone(value);
}
System.out.printf("이메일(%1$s):", m.getEmail());
value = scanner.nextLine();
if (value.length() > 0) {
copy.setEmail(value);
}
System.out.printf("블로그(%1$s):", m.getBlog());
value = scanner.nextLine();
if (value.length() > 0) {
copy.setBlog(value);
}
System.out.printf("나이(%1$d):", m.getAge());
value = scanner.nextLine();
if (value.length() > 0) {
copy.setAge( Integer.parseInt(value) );
}

System.out.print("변경하시겠습니까?(y/n)");
if(scanner.nextLine().toLowerCase().equals("y")){
copy.setRegDate(new Date());
list.set(no-1, copy);
System.out.println("변경되었습니다!");
} else {
System.out.println("변경 취소하였습니다!");
}
}

private void view(int no) {
if (no > 0 && no <= list.size()) {
Member m = list.get(no - 1);
System.out.println("번호:" + no);
System.out.println("이름:" + m.getName());
System.out.println("전화:" + m.getPhone());
System.out.println("이메일:" + m.getEmail());
System.out.println("블로그:" + m.getBlog());
System.out.println("나이:" + m.getAge());
System.out.printf("등록일:%1$tY-%1$tm-%1$td \n", m.getRegDate());
} else {
System.out.println("존재하지 않는 멤버의 번호입니다.");
}
}

public static void main(String[] args) {
// * 인스턴스 생성
// - 개별적으로 값을 다루기 위해, 클래스에 선언된
//   인스턴스 변수를 준비
// - 인스턴스가 사용되기 전에 필요한 작업을 수행하도록
//   생성자 메서드를 호출한다.
RedApp app = new RedApp();
app.execute();
}
}