본문 바로가기

3.구현/Java or Kotlin

[java] POJO, JavaBeans, VO, DTO, PO, BO

들어가기

자바을 활용하다보면 간혹 책이나 검색 자료등에 POJO, VO와 DTO도 많이 보았고 또한 많이 사용하고 있다. 여기서는 여런 용어에 의미를 알아볼려고 한다. 여기서 내용도 절대적이지는 않다. 나름 나만의 해석이나 개인적 주관이 포함되어 있을 수 있지만 한번은 생각해보고 사용해야하지 않을까 생각한다. 그래야 적절한 객체를 정의해서 사용하거나 추후에 확장할 때에 일관된 방향으로 나아갈 수 있을 거라고 생각한다. 이제부터 하나씩 살펴볼려고 한다.

작성자: http://ospace.tistory.com/ (ospace114@empal.com)

POJO란?

POJO는 2000년 9월에 Martin Fowler, Rebecca Parsons 그리고 Josh MacKenzie에 의해서 만들어졌다. POJO는 Plain Old Java Object의 약자로 간단하고 가벼운 평범한 자바 객체를 의미한다. 기존 EJP에서 복잡한고 무거운 객체 정의를 대체하기 위해서 나왔다. 위키에 따르면 일반적인 자바 객체이며, 특정 제약사항이나 다른 클래스 패스가 필요하지 않는다고 한다. 즉, 기본 자바 객체만으로 구성되어 있다고 생각하면 된다. 이 용어는 자바뿐만 아니라 PHP, 닷넷, C++, Perl등에서도 비슷한 용어와 의미로 사용되고 있다.

평범함 자바객체?

평범함 자바객체란 자바언어 사양을 벗어난 범위에 종속하지 않으며, 자바가 있다면 바로 사용할 수 있는 자바 객체라고 할 수 있다. 어떻게 보면 클래스 정의할 때에 외부 라이브러리를 사용하지 않는다는 의미가 된다. 추가적인 라이브러리나 클래스 패스 추가하지 않아도 사용가능해야한다.
예를 들어 javax 패키지에 포함된 클래스나 인터페이스, 그리고 애노테이션를 사용하는 경우 해당 패키지에 종속이되면서 해당 패키지가 없다면 사용할 수 없게 된다. 아래는 잘못된 POJO 사용 예이다.

// 상속
import javax.servlet.http.HttpServle;
class Foo extends HttpServle {
  // ...
}

// 인터페이스 구현
import javax.ejb.EntityBean;
class Bar extends EntityBean {
  // ...
}

// 애노테이션
import javax.persistence.Entity;
@Entity class Baz {
  // ...
}

JavaBeans란?

JavaBeans는 POJO에 직렬화(serialize)와 인자없는 기본 생성자를 가지고 있다. 또한 내부 속성을 액세스하기 위한 getter와 setter를 가지고 있다. POJO와 비교해서 가장 큰 특징은 Serializable 인터페이스를 상속한다. 직렬화를 하는 이유는 자바 객체를 외부로 보내거나 외부에서 받아서 처리할 수 있다. 간단한 예로 네트워크를 통해서 객체를 송수신할 수 있다.

import java.io.Serializable;
class Foo implements Serializable {
  private static final long serialVersionUID = 1L;
  public Foo() {}
  // ...
}

VO란?

VO는 Value Object라고 하며 변경이 불가능한 읽기전용 객체라고 할 수 있다. 이른 특징은 대부분 값이 갖고 있는 특징이다. 대표적인 예가 Integer와 String이 있다. 이는 값을 가지고 있고 가볍고 작은 객체 형태로 많이 사용한다. 대표적인 예로 Money, Date, Range, Color 등이 있다.

단순히 말하면 setter는 없고 getter만 있다고 보면 된다. 이렇게 말하면 값을 변경하지라는 말로 들린다. 그때에는 변경된 값을 가진 객체를 생성하면 된다. 이는 java.lang.String이나 java.lang.Integer를 보면 알 수 있다. 해당 자바 객체는 내부의 값을 변경하는 것이 아니라, 새로운 값을 연산을 통해서 생성되고 저장된다.

RGB 클래스

먼저 RGB라는 간단한 VO 클래스를 만들어보자. 간단하게 만들어서 별다른 내용은 없다. 생성자에 필요한 값을 넘겨서 생성한다. 물론 add()나 subtract()에 의해서 새로운 객체를 생성하기도 한다. 내부적으로 새로 객체를 생성하고 그때 값을 저장해서 반환한다.
생각보다 어렵지 않다. 값을 조회하는 전용이기 때문에 복잡한 도메인 객체에 사용하기에는 적합지 않고 복잡한 데이터를 다루기에도 적합하지 않다. 그렇기에 범용적으로 사용하기 힘들다.

public class RGB {
    private int red;
    private int green;
    private int blue;

    public RGB(int red, int green, int blue) {
        this.red = red;
        this.green = green;
        this.blue = blue;
    }

    public int getRed() {
        return red;
    }

    public int getGreen() {
        return green;
    }

    public int getBlue() {
        return blue;
    }

    public RGB add(RGB other) {
        return new RGB(red + other.getRed(), green + other.getGreen(), blue + other.getBlue());
    }

    public RGB subtract(RGB other) {
        return new RGB(red - other.getRed(), green - other.getGreen(), blue - other.getBlue());
    }
}

이는 구현의 한 종류일 뿐이다. 좀더 생각해보면 굳이 getter까지 필요할까라는 생각이든다. 보통 OOP에서는 권장하지 않지만 내부 속성을 직접 접근하는 방식이다. 필자는 간혹 사용하는 방식이다.

public class RGB {
    public final int red;
    public final int green;
    public final int blue;

    public RGB(int red, int green, int blue) {
        this.red = red;
        this.green = green;
        this.blue = blue;
    }

    public RGB add(RGB other) {
        return new RGB(red + other.red, green + other.green, blue + other.blue);
    }

    public RGB subtract(RGB other) {
        return new RGB(red - other.red, green - other.green, blue - other.blue);
    }
}

DTO란?

DTO란 Data Transfer Object라고 한다. 주요 기능은 값을 보관해서 다른 곳으로 전달 위한 목적이다. 이런 경우는 네트워크를 통한 전송이나 DB에 저장 또는 조회 등이 있다. DTO가 비즈니스 객체(도메인 객체)와 비슷하게 또는, DTO를 비즈니스 객체처럼 사용하는 경우도 있다. 이 둘간에 차이점은 비즈니스 로직의 유무가 있다. 당연히 DTO에는 없다. 물론 DTO에 비즈니스 로직을 포함하는 경우도 있다. 그렇게 되면 비즈니스 객체라고 할 수도 있다.

Bitmap 클래스

다음은 Bitmap이라는 DTO 클래스 정의이다. DTO 클래스는 setter와 getter가 포함되어 있다.

import java.util.Arrays;

public class Bitmap {
    private RGB[] pixels = null;

    public Bitmap(int size) {
        pixels = new RGB[size];
    }

    public RGB getPixel(int idx) {
        return pixels[idx];
    }

    public void setPixel(RGB rgb, int idx) {
        pixels[idx] = rgb;
    }

    public int getSize() {
        return pixels.length;
    }

    public void setSize(int size) {
        this.pixels = Arrays.copyOf(this.pixels, size);
    }
}

외부에서 내부 객체를 직접 조작할 수 있다. 이런 부분에 대해서 getter/setter 메소드가 아무런 역할을 못하는 무용론도 있지만, 필요할 경우 추가적인 처리할 여지를 준다. 물론 모든 멤버 속성에 대해서 getter와 setter가 있어야할 필요는 없다. 필요에 의해 선택적으로 정의하면 된다.

PO

PO는 Persistent Object로 DB 테이블과 링크된 자바 객체이다. JPA에서 사용되는 객체이다. PO에서 엔티티(Entity)와 값객체(Value Object)로 형태로 사용된다. 여기서 값 객체는 앞에 VO와 같은 개념이지만 좀더 확장되서 의미있는 값 집합 단위라는 의미를 포함하고 있다.

BO

BO는 Business Object로 J2EE 패턴에 등장한다. DO는 비즈니스 로직이 집중한 객체라고 할 수 있다. 이 패턴의 목적은 오브젝트 모델로 데이터와 로직을 분리하는데 있다.

결론

지금까지 자바에서 사용하고 있는 개체들의 종류를 살펴보았다. 기본적인 개념을 살펴보았고 이를 기본으로해서 확장해서 다르게 사용할 수도 있다. 앞에 다룬 개념은 반드시 지켜야하는 개념은 아니지만 정확히는 알고 사용하게 좋다고 생각한다. 다양한 라이버리와 프레임워크에서 사용되는 객체들은 서로 각자만의 방식으로 구현되서 사용되고 있다. POJO도 처음에 무거운 EJP 객체에 문제가 있다고 해서 나왔다. 스프링 프레임워크가 인기가 있게 되었던 이유이기도 하다. 그러나 현재는 다양한 객체가 나오고 있다. 해당 라이브러리와 프레임워크의 의도를 잘 이해하고 있다면 더 잘 사용할 수 있지 않을까 생각이든다. 그런 과정에 제 글이 도움이 될 수 있다면 좋네요. 모든 즐거운 코딩생활 하세요.^^ ospace.

참조

[1] https://stackoverflow.com/questions/1612334/difference-between-dto-vo-pojo-javabeans

[2] Plain old Java object, https://en.wikipedia.org/wiki/Plain_old_Java_object

[3] Difference Between POJO, JavaBeans, DTO and VO, https://www.baeldung.com/java-pojo-javabeans-dto-vo

[4] Business Object, http://www.corej2eepatterns.com/Patterns2ndEd/BusinessObject.htm

반응형