'서류'라는 개념이 없는 세상에서 회사에 입사했다고 가정해보자. 실시간으로 전화를 하고, 업무 브리핑을 하고, 그 공간에 없는 사람들은 내용을 모를테니 또 설명하고 또 설명하고..... 상상만해도 지옥이다. (동기방식의 지옥) 인간이 글을 만들어 낸 이유는 기록하고, 그 기록을 다른 사람에게도 쉽게 전달하기 위함이다. 정보의 공유가 쉬워지는 것이다.
DAO는 일종의 서류와도 같다. DB에 접근하여 각 계층마다 정보를 전달해주는 서류다. 정보를 전달해주려면 데이터를 읽을 줄 알아야하고, 그걸 또 읽어와 전달해 줄 수 있어야한다.
또 한가지 예를 들어보자. 요리를 하려고 하는데 파가 똑 떨어졌다. 원시적으로 생각했을때 우린 농장에 가서 파를 사와야할 것이다. 하지만 현대인인 우리는 직접 농장에 방문하지 않고 파를 구할 수 있다. 바로 '마트'라는 시스템을 이용하는 것이다.
마트에 진열된 파는 다 원산지(농원)가 다르지만, 일관된 패키지에 일관된 가격으로 판매되고 있다. 소비자인 우리는 마트를 신뢰하므로 유통경로고 뭐고 아무것도 신경쓰지않고 파를 구매할 수 있다.
DAO는 마트시스템과 비슷하다. 데이터의 저장형태(파의 원산지)는 다 다르지만, 내용의 형식은 동일(다 똑같은 파)하다. 그런 다양한 형태의 데이터(파)들을 일관된 방법(마트 시스템)으로 프로그램에서 읽어 들일 수 있는 기능(소비자에게 판매)을 구현한 것이다.
DAO(Data Access Object) 패턴은 표준 J2EE 디자인 패턴들 중 하나로서 다음과 같은 요소들이 있다.
이 패턴을 사용하여 저수준의 데이터 엑세스(기계에 가까운것을 저수준이라 한다. 순수 데이터 접근처리라 볼 수 있겠다.)와 고급 비즈니스 로직(프론트, UI쪽을 의미한다. 실습파일 Main.) 을 분리할 수 있다.
(분리하면 뭐가 좋나요? 서버 터졌다. 할떄 백엔드파트는 야근하지만 프론트엔드는 퇴근한다. 디자인 전면개편합시다. 프론트파트는 야근하지만 백엔드파트는 집으로 간다)
2020-12-18 // 관심사의 분리. 스프링을 배우고 나니 이제야 조금씩 더 보인다.
package study.java.model;
/** 성적표의 데이터 구조를 표현하는 JavaBeans */
public class Grade {
// 한 건에 대한 데이터를 구조화한다.
// 이 객체가 컬렉션에 저장되면 성적 리스트가 된다.
private String name;
private int kor;
private int eng;
private int math;
public Grade(String name, int kor, int eng, int math) {
super();
this.name = name;
this.kor = kor;
this.eng = eng;
this.math = math;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getKor() {
return kor;
}
public void setKor(int kor) {
this.kor = kor;
}
public int getEng() {
return eng;
}
public void setEng(int eng) {
this.eng = eng;
}
public int getMath() {
return math;
}
public void setMath(int math) {
this.math = math;
}
@Override
public String toString() {
return "Grade [name=" + name + ", kor=" + kor + ", eng=" + eng + ", math=" + math + "]";
}
}
package study.java.dao;
import java.util.List;
import study.java.model.Grade;
/** 학생별 성적 리스트 구현에 대한 설계 */
public interface GradeDao {
/**
* 데이터파일을 읽어들인 후, 파일 안에 저장된 데이터를
* JavaBeans 객체로 변환하여 List에 담아 리턴한다.
* @return Grade형의 객체가 담긴 컬렉션
*/
public List<Grade> getGradeList();
}
package study.java.dao.impl;
import java.util.ArrayList;
import java.util.List;
import study.java.dao.GradeDao;
import study.java.helper.FileHelper;
import study.java.model.Grade;
public class FileGradeDaoImpl implements GradeDao {
/**
* 어떤 객체가 생성되었는지 확인하기 위하여 클래스 이름을
* 출력한다.
*/
public FileGradeDaoImpl() {
String clsName = this.getClass().getCanonicalName();
System.out.println("------" + clsName + "------");
}
@Override
public List<Grade> getGradeList() {
// 리턴할 객체 선언
List<Grade> list = null;
// CSV파일 읽기
String source = FileHelper.getInstance().readString(
"res/grade.csv", "euc-kr");
// 읽은 내용이 없다면 강제 종료
if (source == null) {
return null;
}
// 읽은 내용이 있다면 리턴할 List를 할당
list = new ArrayList<Grade>();
/** (1) 라인단위로 분리 --> 학생별 데이터 */
String[] data = source.split("\n");
/** (2) 학생 수 만큼 반복 처리 */
for (int i = 0; i < data.length; i++) {
/** (3) 한 명의 데이터 추출 */
String line_string = data[i].trim();
/** (4) 한 명의 데이터를 과목별로 분리 */
String[] line_data = line_string.split(",");
/** (5) 분리한 데이터 추출 */
String name = line_data[0];
int kor = Integer.parseInt(line_data[1]);
int eng = Integer.parseInt(line_data[2]);
int math = Integer.parseInt(line_data[3]);
/** (6) 분리한 데이터를 객체로 변환 후 컬렉션에 추가 */
Grade item = new Grade(name, kor, eng, math);
list.add(item);
}
// 리턴값을 선언된 객체로 변경
return list;
}
}
package study.java.dao.impl;
import java.util.ArrayList;
import java.util.List;
import org.json.JSONArray;
import org.json.JSONObject;
import study.java.dao.GradeDao;
import study.java.helper.FileHelper;
import study.java.model.Grade;
public class JsonGradeDaoImpl implements GradeDao {
/**
* 어떤 객체가 생성되었는지 확인하기 위하여 클래스 이름을
* 출력한다.
*/
public JsonGradeDaoImpl() {
String clsName = this.getClass().getCanonicalName();
System.out.println("------" + clsName + "-------");
}
@Override
public List<Grade> getGradeList() {
// 리턴할 객체 선언
List<Grade> list = null;
// JSON파일 읽기
String source = FileHelper.getInstance().readString(
"res/grade.json", "utf-8");
// 읽은 내용이 없다면 강제 종료
if (source == null) {
return null;
}
// 읽은 내용이 있다면 리턴할 List를 할당
list = new ArrayList<Grade>();
/** (1)읽은 내용을 JSON 객체로 변환 */
JSONObject json = new JSONObject(source);
/** (2) 배열데이터 추출 --> 학생별 데이터 */
JSONArray grade = json.getJSONArray("grade");
/** (3) 학생 수 만큼 반복처리 */
for (int i = 0; i < grade.length(); i++) {
/** (4) 한명의 데이터 추출 */
JSONObject temp = grade.getJSONObject(i);
/** (5) 데이터 분리 */
String name = temp.getString("name");
int kor = temp.getInt("kor");
int eng = temp.getInt("eng");
int math = temp.getInt("math");
/** (6) 분리된 데이터를 객체로 변환 후 컬렉션에 추가 */
Grade item = new Grade(name, kor, eng, math);
list.add(item);
}
// 리턴값을 선언된 객체로 변경
return list;
}
}
package study.java.helper;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
public class FileHelper {
// ------ 싱글톤 객체 생성 시작 ------
private static FileHelper current;
public static FileHelper getInstance() {
if (current == null) {
current = new FileHelper();
}
return current;
}
public static void freeInstance() {
// 객체에 null을 대입하면 메모리에서 삭제된다.
current = null;
}
// 기본 생성자를 private으로 은닉하게 되면 new를 통한 객체 생성이 금지된다.
private FileHelper() {
super();
}
// ------ 싱글톤 객체 생성 끝 ------
/**
* 주어진 경로에 data 값을 기록하고 저장한다.
*
* @param filePath - 저장할 파일 경로
* @param data - 저장할 내용을 담은 스트림
* @return boolean - 성공시 true, 실패시 false
*/
public boolean write(String filePath, byte[] data) {
boolean result = false;
/** 파일 저장 절차 시작 */
// finally 블록에서 인식하기 위해서 선언부를 위로 이동시킨다.
OutputStream out = null;
try {
out = new FileOutputStream(filePath);
// 파일쓰기
out.write(data);
System.out.println("[INFO] 파일 저장됨 >> " + filePath);
// 저장에 성공하였으므로, 결과값을 true로 변경
result = true;
} catch (FileNotFoundException e) {
System.out.println("[ERROR] 지정된 경로를 찾을 수 없음. >> " + filePath);
e.printStackTrace();
} catch (IOException e) {
System.out.println("[ERROR] 파일 저장 실패 >> " + filePath);
e.printStackTrace();
} catch (Exception e) {
System.out.println("[ERROR] 알 수 없는 에러 >> " + filePath);
e.printStackTrace();
} finally {
if (out != null) {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return result;
}
/**
* 주어진 경로의 파일을 읽고, 내용을 스트림으로 리턴한다.
*
* @param filePath - 읽어야 할 파일의 경로
* @return 읽혀진 내용
*/
public byte[] read(String filePath) {
byte[] data = null;
/** 파일 읽기 */
InputStream in = null;
try {
in = new FileInputStream(filePath);
// 읽은 내용을 담기 위한 배열은 파일의 용량만큼 사이즈를 할당한다.
// in.available() --> 열고 있는 파일의 크기
data = new byte[in.available()];
// 파일 읽기 - 파라미터로 전달된 배열 안에, 파일의 내용을 담아준다.
in.read(data);
System.out.println("[INFO] 파일 읽기 성공 >> " + filePath);
} catch (FileNotFoundException e) {
System.out.println("[ERROR] 지정된 경로를 찾을 수 없음. >>" + filePath);
e.printStackTrace();
} catch (IOException e) {
System.out.println("[ERROR] 파일 읽기 실패 >> " + filePath);
e.printStackTrace();
} catch (Exception e) {
System.out.println("[ERROR] 알 수 없는 에러 >> " + filePath);
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
} // end try~catch~finally
return data;
}
/**
* 파일을 저장한다.
*
* @param filePath - 저장할 파일 경로
* @param content - 저장할 내용
* @param encType - 인코딩 형식
* @return boolean - 성공시 true, 실패시 false
*/
public boolean writeString(String filePath, String content, String encType) {
boolean result = false;
byte[] data = null;
try {
data = content.getBytes(encType);
} catch (UnsupportedEncodingException e) {
System.out.println("[ERROR] 인코딩 지정 에러");
e.printStackTrace();
} catch (Exception e) {
System.out.println("[ERROR] 알 수 없는 에러 >> " + filePath);
e.printStackTrace();
}
result = this.write(filePath, data);
return result;
}
/**
* 파일의 내용을 문자열로 리턴한다.
*
* @param filePath - 읽어야 할 파일의 경로
* @param encType - 인코딩 형식
* @return String - 읽은 내용에 대한 문자열
*/
public String readString(String filePath, String encType) {
String content = null;
byte[] data = this.read(filePath);
// 내용을 문자열로 변환한다.
try {
content = new String(data, encType);
content = content.trim();
} catch (UnsupportedEncodingException e) {
System.out.println("[ERROR] 인코딩 지정 에러");
e.printStackTrace();
} catch (Exception e) {
System.out.println("[ERROR] 알 수 없는 에러 >> filePath");
e.printStackTrace();
}
return content;
}
}
import java.util.List;
import study.java.dao.GradeDao;
import study.java.dao.impl.FileGradeDaoImpl;
import study.java.model.Grade;
public class Main01 {
public static void main(String[] args) {
GradeDao dao = new FileGradeDaoImpl();
List<Grade> list = dao.getGradeList();
for (int i=0; i<list.size(); i++) {
Grade item = list.get(i);
System.out.println(item.toString());
}
}
}
import java.util.List;
import study.java.dao.GradeDao;
import study.java.dao.impl.JsonGradeDaoImpl;
import study.java.model.Grade;
public class Main02 {
public static void main(String[] args) {
GradeDao dao = new JsonGradeDaoImpl();
List<Grade> list = dao.getGradeList();
for (int i=0; i<list.size(); i++) {
Grade item = list.get(i);
System.out.println(item.toString());
}
}
}
```
이 포스트는 itpaper.co.kr에서 제공되는 강의자료를 바탕으로 작성되었습니다.