DAO

공병주(Chris)·2022년 4월 13일
1


우테코 체스 게임을 통해, 처음으로 DAO 객체를 통한 DB Connection을 경험해보았습니다.

DAO의 개념DB 접근을 DAO로 분리하는 이유에 대해서 알아보겠습니다.

DAO란?

아래와 같이, Data Access Object(데이터 접근 객체)라는 뜻으로 DB에 값을 저장하고, DB에서 값을 꺼내 controller나 service 객체에 전달하는 책임을 지닙니다.

public class PieceDao {

    public void savePiece(final Position position, final Piece piece) {
        final String sql = "insert into piece (position, team, name) values (?, ?, ?)";

        try (final PreparedStatement statement = getConnection().prepareStatement(sql)) {
            statement.setString(1, position.toString());
            statement.setString(2, piece.getTeam());
            statement.setString(3, position.getName());
			statement.executeUpdate();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

DB접근 책임을 DAO로 분리시키는 이유

1. 책임의 분리

객체 지향 패러다임에서는, 공통된 목표를 위한 기능들을 여러 책임으로 분리하고 책임을 여러 객체들에게 나눕니다. 분리된 책임 중에, DB Connection에 대한 책임을 DAO가 지니는 것입니다.

더 나아가, DB에 테이블이 여러 개라면, 한 테이블에 대해 접근하는 책임을 하나의 객체에게 부여할 수도 있습니다.

2. 변경에 유연하게 대처(확장성)

아래와 같이 Oracle DB에 접근하는 Dao를 사용하고 있다고 가정해봅시다.

public class PieceDaoOracle {

    public void savePiece(final Position position, final Piece piece) {

	}
}

후에, MySQL로 DB가 변경된다면 어떻게 할까요?

DB에 대한 접근 책임을 DAO로 분리해두면 아래와 같이 인터페이스를 통해서 변경에 대응할 수 있습니다.

public interface PieceDao {
    void savePiece(final Position position, final Piece piece);
}

PieceDao 라는 인터페이스로 추상화를 시켜두고, 필요에 따라 아래와 같은 구현체들을 갈아 끼울 수 있습니다.

public class PieceDaoOracle implements PieceDao {

	@Override
    public void savePiece(final Position position, final Piece piece) {
					//Oracle DB Connection
	}
}
public class PieceDaoMySQL implements PieceDao {

	@Override
    public void savePiece(final Position position, final Piece piece) {
					//MySQL DB Connection
	}
}

하지만, DAO가 가지는 책임을 분리시키지 않고 Service 객체가 지니도록 한다면 아래와 같이
Service 객체를 interface로 분리하고, DB 종류에 따른 Service 구현 객체들을 만들어야 합니다.

public interface ChessGameService {
    void movePiece(final Position source, final Position target);
}
public interface ChessGameServiceMySQL {
		
	@Override
    public void movePiece(final Position source, final Position target) {
				//MySQL DB로 이뤄지는 Service 로직
	}
}
public interface ChessGameServiceOracle {
		
	@Override
    public void movePiece(final Position source, final Position target) {
				//Oracle DB로 이뤄지는 Service 로직
	}
}

3. 테스트에 대한 편의성

Service 객체에서 Service 로직에 대한 테스트를 작성하는 경우를 생각해봅시다.
Service의 로직에는 DAO를 통해 DB 접근이 있습니다.

그렇다면, 테스트의 데이터들이 DB를 오염시킬 것입니다.

이런 경우에 아래와 같이, DB에 대한 접근을 DAO로 분리하고 Service 객체에 DAO를 주입해주면 문제를 해결할 수 있습니다.

1. 테스트를 위한 DB에 접근하는 DAO

테스트를 위한 DB를 생성하고, 해당 DB에 접근하는 DAO를 Service 객체에 주입해주면,
테스트 과정에서, 프로덕션에 대한 DB는 오염되지 않습니다.

public class ChessGameService {
		
	private final PieceDao pieceDao;

	public ChessGameService(final PieceDao pieceDao) {
			this.pieceDao = pieceDao;
	}

    void movePiece(final Position source, final Position target) {
    
    }
}
class ChessGameSerciceTest {
		private final ChessGameService chessGameService = 
        							new ChessGameService(new PieceDaoFake);

}
public class PieceDaoTestDB implements PieceDao {
			//프로덕션 DB가 아닌 Test를 위한 DB에 Connection
}

2. DB 처럼 행동하는 가짜 객체 주입

Service 객체에 대한 테스트를 진행하는데, DB가 실제로 Connection 되어 있다면
Test 과정에서 실제로 DB에 연결하고 접근을 해야하기 때문에 테스트 성능이 떨어집니다.

Service 테스트에서는, DB에 실제로 연결이 되어있는지 아닌지는 중요하지 않습니다.
Service 로직이 잘 동작하는지 확인만 하면 됩니다.

따라서, 아래와 같이 내부에는 DB Connection이 이뤄지지 않은 DAO를 Service 객체에 주입해준다면,
Service 객체의 테스트 성능을 높일 수 있습니다.

public class ChessGameService {
		
	private final PieceDao pieceDao;

	public ChessGameService(final PieceDao pieceDao) {
			this.pieceDao = pieceDao;
	}

    void movePiece(final Position source, final Position target) {
    
    }
}
class ChessGameSerciceTest {

		private final ChessGameService chessGameService = 
        							new ChessGameService(new PieceDaoFake);

}
public class PieceDaoFake implements PieceDao {

		private final Map<String, Piece> fakeDB = new HashMap<>();
}

PieceDaoFake 객체의 fakeDB 필드가 DB의 역할을 대체할 수 있습니다.

Service 객체에 대한 Test 과정에서 실제로 DB Connection이 이뤄지지 않기 때문에,
프로덕션 DB가 오염되지 않고, 테스트 성능을 끌어올릴 수도 있습니다.


어떤 분의 재밌는 농담으로 이 글을 마무리 하겠습니다.

출처 : https://okky.kr/article/153941

0개의 댓글

관련 채용 정보