'밥벌이/Java'에 해당되는 글 4건

  1. 2020.07.16 VO, DTO 구분해서 사용해야 할까?
  2. 2015.09.18 XML파일의 활용
  3. 2015.09.18 BufferedStream 팁
  4. 2015.09.18 Properties 파일 읽기

VO 와 DTO 는 둘 다 모델에 해당하는 객체로 그 차이는 단순하다.

VO는 불변객체이고, DTO는 가변객체이다.

가변객체는 그냥 getter, setter 가 포함된 bean 을 생각하면 되고

불변객체는 setter를 포함하지 않아서 변화를 허용하지 않는 걸 말한다.

이에 DTO에는 데이터를 변환하는 비즈니스 로직이 포함될 수도 있다.

 

지금까지 DTO 같은 성격의 객체는 실무에서 많이 사용했다. 

그런데 VO 성격의 객체는 거의 사용해본 적이 없다.

불변객체...대체 언제 필요할까? 솔직히 없어도 된다.

그래도 굳이 나눠지는 개념이니 한번 생각해보자.

 

필요하긴 할거다. 접근제한자로 private 도 쓰지 않는가? 

변화를 허용하지 않기 위해 private 로 불변자원을 만든다.

객체도 "내가 처음에 만들어준 데이터 바꾸지 마" 라고 지정할 수 있는거다. 

 

프로젝트에서 VO 같은 사용자 정의 객체가 사용되는 경우를 생각해보자. 

1. DB select 를 담는 경우

2. Controller 파라미터를 담는 경우

 

그 외 비즈니스 로직에서 별도로 사용자 정의 객체가 사용되는 경우도 있겠지만

있다고 하더라도 로직에 의해 값이 유동적으로 변화하는 경우가 많기 때문에 불변인 VO로 볼 수 없다.

 

1번의 경우는 가져온 데이터를 정재하는 경우가 있기 때문에 VO로 적합하지 않다.

Controller 파라미터는 VO가 가능할 거 같다. 

그렇다면 Controller 파라미터를 vo로 해서 가질 수 있는 장점은 뭘까?

장점이 있어야 쓸 거 아닌가..

 

생각나는 장점 중 하나는 해당 VO객체가 거치게 되는 메소드나 클래스가 많을 경우

불변이 보장된다면 추후 시스템 분석이나 버그 디버깅에 도움이 될 수 있다.

일반적으로 빌링 또는 커머스시스템은 굉장히 복잡한 로직을 품고 있는데

로직이 수행되는 중에 값이 변화하지 않는다는 보장만 있다면 해당 경우의 수만 없애는 것만으로도

디버깅에 굉장히 큰 도움이 된다.

 

다만 모델객체를 구현하는 방식에 따라 가변, 불변을 모두 제공할 수 있다.

즉, 일부 데이터만 포함한 생성자에 빌더패턴을 적용하고 수정을 허용할 데이터는

별도로 setter를 만들어 주면 된다.

이렇게 되면 빌더패턴에 포함된 데이터는 불변이 되고(변경하려면 객체를 새로 생성해야 함)

포함되지 않은 데이터는 가변이 된다.

 

그렇다면 또 다시 VO, DTO를 구분하지 않고 이렇게 하나의 객체로 사용할 때 

가질 수 있는 장점은 뭐가 있을까?

가장 먼저 생각나는 것은 로직당 통일된 하나의 모델을 사용할 수 있다는 것이다.

 

예를 들어 유저정보를 수정하는 기능을 아래와 같이 만든다고 생각해보자.

 

변경될 유저정보를 받아서 그 값으로 DB 테이블을 업데이트하고 

정상적으로 업데이트 되었다면 그 값을 다시 리턴하고

업데이트에 실패했다면 DB상 최종데이터를 조회해서 리턴한다.

 

VO, DTO 를 구분할 경우 변경될 유저정보를 받을 UserVO 객체가 필요하고

DB 데이터를 담을 UserDTO 라는 객체가 필요하게 된다.

이러한 VO, DTO 는 비슷한 성격이기 때문에 아무래도 중복코드가 발생할 수 밖에 없다.

하지만 일부에만 빌더패턴을 적용한 객체를 사용할 경우 

UserModel 이라는 하나의 객체로도 충분하게 된다.

 

결국 가변, 불변 기능을 모두 포함한 모델객체를 사용하는게 가장 좋아보인다.

 

테스트 삼아 한번 만들어보자. 유저에 해당하는 모델을 만들 것이다.

 

조건은 아래와 같다.

사용할 데이터는 아이디, 이름, 나이, 등급으로 하고

이 중 아이디, 이름, 나이는 불변이고 등급은 가변으로 할 것이다.

더 나아가 아이디, 이름은 필수값으로 할 것이다.

 

완성된 모델은 아래와 같다.

@Getter
@ToString(exclude={""})
@EqualsAndHashCode(of = {"id", "name", "age"})
public class UserModel {

    @NotBlank(message = "id is required")
    private String id;
    @NotBlank(message = "name is required")
    private String name;
    private int age;
    private String grade;

    @Builder
    private UserModel(String id, String name, int age, String grade){
        Assert.hasText(id, "id is required");
        Assert.hasText(name, "name is required");
        this.id = id;
        this.name = name;
        this.age = age;
        this.grade = grade;
    }

    public void setGrade(String grade) {
        this.grade = grade;
    }
}

마지막으로 이것을 쓰는 것이 단점은 뭐가 있을까?

현재로선 없어보이는데 생각나면 추가 기록하겠다.

 

'밥벌이 > Java' 카테고리의 다른 글

XML파일의 활용  (0) 2015.09.18
BufferedStream 팁  (0) 2015.09.18
Properties 파일 읽기  (0) 2015.09.18
Posted by mypiece
,

XML파일의 활용

밥벌이/Java 2015. 9. 18. 22:21

1. xml 파일 생성하기

- 일반적으로 xml 파일은 정적인 설정정보를 저장하기 위한 용도로 사용되며 

어플리케이션에서 해당 정보를 읽어서 사용한다. 

이러한 이점으로 인해 자주 변경되야 하는 설정정보의 경우 xml파일로 작성하여 

수정이 있을 때 별도의 소스 컴파일 과정없이 확장이 가능하게 된다.

다수의 설정정보를 저장하는 만큼 파일 내에서 필요한 데이터를 

효율적으로 파싱하기 위해 기본적인 구조는 트리구조를 가지고 있다.

 

기본적인 작성형식은 아래와 같다.

<?xml version=”1.0” encoding=”UTF-8”?>

<movieList>

<movie>

<name>신의한수</name>

<creator>조범구</creator>

<genre>액션</genre>

</movie>

<movie name = “트랜스포머”>

<option name = “creator">마이클베이</option>

<option name = “genre”>SF</option>

</movie>

</movieList>

  

- <?xml version=”1.0” encoding=”UTF-8”?> 의 경우 해당 파일이 xml 파일임을 

명시하는 것으로 반드시 최상단에 들어가야 한다. encoding 값 설정의 경우 선택적이긴 

하지만 한글을 사용할 것이라면 위와 같이 UTF-8로 지정해 주는 것이 좋고, 대부분 그렇게 사용한다.

* 주의할 점은 XML 파일에서는 대소문자를 철저히 구분하므로 

위 예시처럼 대소문자를 가리지 않거나 “”를 생략한다면 에러가 발생한다.

 

- 아랫 줄 부터는 실제 사용할 정보를 기술한다. 

<movieList></movieList>와 같이 반드시 종료태그를 기술해야 한다.

 

- <movieList> 와 같이 가장 바깥쪽에 있는 태그를 Root Element라고 하며 

반드시 1개만 존재할 수 있다. 1개 이상 존재한다면 읽어올 때 에러가 발생한다.

 

-  아래로 보이는 <movie> 태그로 2개의 영화에 대한 정보를 기술하였다.

정보 기술의 경우 첫번째 영화(신의한수)처럼 단순노드만으로도 구성이 가능하며 

두번째 영화(트랜스포머)처럼 속성을 포함한 노드로도 구성이 가능하다.

 

- 처음에 말했듯이 XML은 기본적으로 트리구조를 가지고 있기 때문에 

각 정보에 접근하는 방식은 node와 element로서 접근하게 된다. 

node는 단순히 해당 경로의 접점을 의미하며 element의 경우 경로와 

요소를 포함한 객체를 의미한다. 

이러한 이유로 위 xml에서 '신의한수' 라는 정보를 얻고자 한다면 

<move> -> <name> 와 같이 node만으로 접근하고 해당 텍스트를 얻어오면 되지만 

'트랜스포머'라는 정보를 얻고자 한다면 <movie> -> <option> node로 접근한 뒤 

element로 형변환한뒤에 name 속성에 해당하는 을 얻어와야 한다.

 

 



2. xml 파일 읽기
- 기본적으로 XML 파일을 읽기 위해서는 아래의 과정을 거친다.

 모든 방식의 접근방식을 기술한 것이 아니라 본인이 활용하거나 습득한 정보만

나열되어 있기 때문에 하나의 예시로써 활용하고 추가로 필요한 부분은 검색 또는 API를 참고해야 할 것 같다.


읽어올 XML 파일의 내용은 아래와 같다.

<?xml version=”1.0 encoding=”UTF-8”?>

<confList>

<confResource name = “ExcelCompare”>

<option name = “basePath”>d:/업무/요청자료/</option>

<option name = “target_fileName”>전체 사용자목록.xls</option>

<option name = “standard_fileName”>A팀 현재 사용자목록.xls</option>

<option name = “result_fileName”>A팀 정상 사용자목록.xls</option>

</confResource>

<confResource name = “...”>

...

</confResource>

</confList>


해당 설정파일의 경우 ‘A팀 현재 사용자목록’ 중 ‘전체 사용자목록’에 포함되지 않는 사용자들을 비교하여

삭제한 뒤 결과 엑셀파일을 새로 생성하는 자바어플리케이션 개발시 사용되었다.
추후 <confResource> 태그를 하단에 추가하여 name 속성에 각각의 프로세스명을 기입하여

다른 자바어플리케이션 개발시에도 함께 활용할 계획이었다.



2.1. Document 객체 생성


DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();


// classpath 상에 존재하는 설정파일을 읽어올 때
InputStream is = getClass().getResourceAsStream(“/conf/DestinyConf.xml”);
Document d = db.parse(is);


/*
classpath 상에 존재하지 않는 설정파일을 읽어올 때
File confFile = new File(“d:/conf/DestinyConf.xml”);
Document d = db.parse(confFile);
보이는 것 처럼 DocumentBuild의 parse() 메소드는 오버로딩 되어있어 InputStream, File 중 아무거나 받을 수 있다.
*/


d.getDocumentElement().normalize();



2.2. Root Element 값 얻기


d.getDocumentElement().getNodeName();



2.3. 설정파일 리스트 얻어오기


NodeList confList = d. getElementsByTagName (“confResource”);

- confResource 라는 태그명을 가지는 모든 node 리스트를 얻어온다.



2.4. 설정파일 리스트에서 현재 프로세스에서 사용할 설정파일에 맞는 자원 찾기

for(int i = 0; i < confList.getLength(); i++){
/* confResource라는 태그명을 가진 node를 순차적으로 돌면서 해당 프로세스의 설정정보로 지정된 node를 찾는다. */


 Element confResource = (Element)confList.item(i);
 /* 단순히 node로 접근하는 것이 아닌 name속성을 체크해야 하므로 Element 로 변환한다. */


 if(confResource.getAttribute(“name”).equals(“ExcelCompare”)){
 /* name 속성이 해당 프로세스인 것을 찾는다. */


  NodeList confResourceList = confResource.getElementsByTagName(“option”);
  /* 해당 노드 안에는 프로세스에서 사용할 option 값들이 여러 노드로 분배되어 있기 때문에

      option 태그를 가지는 node들로 리스트를 생성한다. */


  for(int j = 0; j < confResourceList.getLength(); j++){
   /* 해당 node 리스트를 돌면서 필요한 설정값을 읽어서 설정한다.*/


   Element option = (Element)confResourceList.item( j);
   String optionName = option.getAttribute( "name");


   if(optionName.equals( "basePath")){
    basePath = option.getTextContent(); //해당 node의 텍스트 자원을 얻어온다.
   }else if(optionName.equals("target_fileName")){
    target_fileName = option.getTextContent();
   }else if(optionName.equals( "standard_fileName")){
    standard_fileName = option.getTextContent();
   }else if(optionName.equals("result_fileName")){
    result_fileName = option.getTextContent();
   }
  }
 }
}

'밥벌이 > Java' 카테고리의 다른 글

VO, DTO 구분해서 사용해야 할까?  (0) 2020.07.16
BufferedStream 팁  (0) 2015.09.18
Properties 파일 읽기  (0) 2015.09.18
Posted by mypiece
,

BufferedStream 팁

밥벌이/Java 2015. 9. 18. 22:20

BufferedStream 의 경우 입출력 결과를 버퍼에 담아놨다가 입출력을 수행하기 때문에
버퍼링 기능을 하게 되고 건건이 입출력하는 것 보다 좋은 성능을 보여준다.

그런 이유로 JAVA에서 Stream을 사용할 경우 그대로 사용하지 않고

BufferedStream과 같은 보조스트림을 구현해서 사용하게 된다.

이때 불필요한 코딩을 줄일 수 있는 팁이 있다.

 

1. BufferedStream을 사용하고 close를 통해 Stream을 닫을 경우

기반이 되었던 Stream은 별도로 close 해줄 필요가 없다.

BufferedStream의 close()메소드에서 내부적으로 기반스트림에 대한

close()메소드를 수행하기 때문이다.

 

2. BufferedStream 의 경우 버퍼가 가득찼을 때만 출력소스에 출력을 하기 때문에

출력이 끝난 뒤 반드시 flush() 해주어 마지막에 버퍼에 있는 모든 내용이 출력될 수 있도록 해야한다.

하지만 출력뒤 바로 Stream을 닫아버리는 상황이라면 close()만 수행하면 되며,
별도로 flush()를 수행할 필요는 없다.
BufferedStream의 close()의 경우 조상인 File Stream의 close()를 그대로 상속받고
File Stream의 close()는 내부적으로 flush()를 수행한다음 close()를 수행하기 때문에
flush()를 수행하는 시점이 Stream을 끝내는 상황이라면 close()만으로
충분하다는 것이다. 

 

'밥벌이 > Java' 카테고리의 다른 글

VO, DTO 구분해서 사용해야 할까?  (0) 2020.07.16
XML파일의 활용  (0) 2015.09.18
Properties 파일 읽기  (0) 2015.09.18
Posted by mypiece
,

1. classpath 상에 파일 불러오기

  

test.properties 파일에는 아래와 같은 내용이 들어있다고 가정함.

key=/com/test/key

 

//step1 : Stream 연결          
InputStream is = getClass().getResourceAsStream("/com/test/test.properties");

/* or Reader

Reader reader = new InputStreamReader(getClass().getResourceAsStream("/com/test/key") );

*/

 

//step2 : 연결된 Stream을 통해 Proerties 객체 load

Properties prop = new Properties();
prop.load(is);


//step3 : 파일에 key 에 값을 가져온다

prop.get("key");

 

※ 동일한 Properties파일이 JAR파일과 Classes에 각각 존재한다면 Classes에 있는 파일을 읽어들인다.

 


2. classpath 가 아닌 외부폴더에서 읽어오기

 

File propFile = new File(“d://com/test/test.properties”);
BufferedInputStream bi = new BufferedInputStream(new FileInputStream(propFile));
Properties prop = new Properties();
prop.load(bi);
prop.get("key");​

 

'밥벌이 > Java' 카테고리의 다른 글

VO, DTO 구분해서 사용해야 할까?  (0) 2020.07.16
XML파일의 활용  (0) 2015.09.18
BufferedStream 팁  (0) 2015.09.18
Posted by mypiece
,