ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • ORM- SQL을 직접 다룰 때 발생하는 문제점
    FrameWork/ORM 2024. 1. 20. 19:52

    SQL을 직접 다룰 때 발생하는 문제점

    관계형 데이터베이스는 가장 대중적이고 신뢰할 만한 안전한 데이터 저장소다. 그런 데이터 베이스를 관리하려면 SQL을 사용해야 한다. 자바로 작성한 애플리케이션은 JBDC API를 사용해서 SQL을 데이터베이스에 전달한다. 

     

    반복 그리고 반복

     구체적인 예시로 SQL을 직접 다룰 때의 문제점을 알아보자 예시의 시나리오는 자바와 관계형 데이터베이스를 사용해서 회원 관리 기능을 구현하는 것이다.

     

     

    회원을 CRUD하는 기능

    public class Member{
        private Stirng memberId;
        private String name;
      ...
    }

     

    public class MemberDAO{
        public Member find(String memberId){...}
    }

     

    이제 MemberDAO의 find() 메소드를 완성해서 회원을 조회하는 기능을 개발해 보자

     

    1. 회원 조회용 SQL을 작성한다.

    SELECT MEMBER_ID,
           `NAME` 
    FROM `MEMBER` M
    WHERE MEMBER_ID = ?

     

    2. JDBC API를 사용해서 SQL을 실행한다.

    ResultSet rs = stmt.executeQuery(sql);

     

    3. 조회 결과를 Member 객체로 매핑한다.

    String memberId = rs.getString("MEMBER_ID");
    String name = rs.getString("NAME");
    
    Member member = new Member();
    member.setMemberId(memberId);
    member.setName(name);

     

    회원 조회 기능을 완성했다. 

     

    회원 등록 기능 추가

    public class MemberDAO{
        public Member find(String memberId){...}
        public void save(Member member){...} //추가
    }

     

    1. 회원 조회용 SQL을 작성한다.

    String sql = "INSERT INTO MEMBER(MEMBER_ID, NAME) VALUE (?,?)";

     

    2. JDBC API를 사용해서 SQL을 실행한다.

    pstmt.setString(1, member.getMemberId());
    pstmt.setString(2, member.getName());

     

    3. JDBC API를 사용해서 SQL을 실행한다.

    pstmt.executeUpdate(sql);

     

     

    회원을 조회하는 기능과 등록하는 기능을 만들었다. 다음으로 회원을 수정하고 삭제하는 기능도 추가해보자. 아마도 SQL을 작성하고 JDBC API를 사용하는 비슷한 일을 반복해야 할 것이다.

    회원 객체를 데이터베이스가 아닌 자바 컬렉션에 보관한다면 어떨까? 컬렉션은 다음 한줄로 객체를 저장할 수 있다.

    list.add(member);

     

    하지만 데이터베이스는 객체 구조와는 다른 데이터 중심의 구조를 가지므로 객체를 데이터베이스에 직접 저장하거나 조회할 수는 없다. 따라서 개발자가 직접 객체지향 언어와 데이터베이스 사이에서 변환작업을 해주어야 하고 이 일에는 무수한 반복 작업이 병행된다. 데이터 접근 계층(DAO)을 개발하는 일은 지루함과 반복의 연속이다.

     


     

    SQL에 의존적인 개발

    앞에서 만든 회원 객체를 관리하는 MemberDAO를 완성하고 애플리케이션의 나머지 기능도 개발을 완료했다. 그런데 갑자기 회원의 연락처도 함께 저장해달라는 요구사항이 추가 되었다. 

     

    등록 코드 변경(회원 클래스에 연락처 필드 추가)

    회원의 연락처를 추가하려고 회원 테이블에는 TEL 컬럼을 추가하고 회원 객체에 tel 필드를 추가했다.

    public class Member{
    	
        private String memberId;
        private String name;
        private String tel; //추가 
        ...
    }

     

    연락처를 저장할 수 있도록 INSERT SQL을 수정했다.

    String sql = "INSERT INTO MEMBER(MEMBER_ID, NAME, TEL) VALUES (?,?,?)";

     

    그 다음 회원 객체의 연락처 값을 꺼내서 등록 SQL에 전달했다. 

    pstmt.setString(3,member.getTel());

     

    연락처를 데이터베이스에 저장하려고 SQL과 JDBC API를 수정했다. 그리고 회원 조회와 수정에 관련된 코드도 수정해 준다. 그러면 데이터 레이어의 수많은 코드를 위와 같이 수정해 주어야 한다.

     

    반면 회원 객체를 데이터베이스가 아닌 자바 컬렉션에 보관했다면 필드를 추가한다고 해서 이렇게 많은 코드를 수정할 필요는 없을 것이다.

    list.add(member); //등록
    Member member = list.get(1); //조회
    member.setTel("xxx"); //수정

     


    연관된 객체

    회원은 어떤 한 팀에 필수로 소속되어야 한다는 요구사항이 추가되었다. 팀 모듈을 전담하는 개발자가 팀을 관리하는 코드를 개발하고 커밋했다. 코드를 받아보니 Member객체에 team필드가 추가되었다.

     

    회원 클래스에 연관된 팀 추가

    class Member{
        
        private String memberId;
        private String name;
        private String tel;
        private Team team; //추가
        ...
    }

     

    class Team{
        ...
        private String teamName;
        ...
    }

     

    다음 코드를 추가해서 화면에 팀의 이름을 출력했다. 

    // 이름
    member.getName();
    // 소속 팀, 추가
    member.getTeam().getTeamName();

     

    코드를 실행해보니 member.getTeam()의 값이 항상 null이다. 회원과 연관된 팀이 없어서 그럴 것으로 생각하고 데이터베이스를 확인해보니 모든 회원이 팀에 소속되어 있다. 문제를 찾다가 밑의 예제코드 처럼 Member DAO에 findWithTeam()이라는 새로운 메소드가 추된 것을 확인했다.

    public class MemberDAO{
    	
        public Member find(Stirng memberId){...}
        public Member findWithTeam(String memberId){...}
    }

     

    MemberDAO 코드를 열어서 확이해보니 회원을 출력할 때 사용하는 find()메소드는 회원마 조회하는 다음과 같은 SQL이 유지 되었다.

    SELECT MEMBER_ID, `NAME`, TEL FROM `MEMBER` M

     

    또한 새로운 findWithTeam() 메소드는 다음 SQL로 회원과 연관된 팀을 함께 조회했다.

    SELECT M.MEMBER_ID, 
           M.`NAME`,
           M.TEL,
           T.TEAM_ID,
           T.TEAM_NAME
    FROM `MEMBER` M
    JOIN TEAM T
    	ON M.TEAM_ID = T.TEAM_ID

    결국 DAO를 열어서 SQL을 확인하고 나서야 원인을 알 수 있었고, 회원 조회 코드를 MemberDAO.find()에서 MemberDAO.findWithTema()으로 변경해서 문제를 해결했다. 

     

    SQL과 문제점을 정리해보자. Member객체가 연관된 Team객체를 사용할 수 있을지 없을지는 전적으로 사용하는 SQL이  제대로 작성 되었느냐에 달려있다. 이런 방식의 가장 큰 문제는 데이터 접근 계층을 사용해서 SQL을 숨겨도 어쩔 수 없이 DAO를 열어서 어떤 SQL이 실행되는지 확인해야 한다는 점이다.

     

    Member나 Team 처럼 비지니스 요구사항을 모델링한 객체를 엔티티라고 하는데, 지금 처럼 SQL에 모든 것을 의존하는 상황에서 엔티티를 신뢰하고 사용할 수 없다. 대신에 DAO를 열어서 어떤 SQL이 실행되고 어떤 객체들이 함께 조회되는지 일일이 확인해야 한다. 따라서 이것은 진정한 의미의 계층 분할이 아니다. 물리적으로 SQL과 JDBC API를 데이터 접근 계층에 숨기는 데 성공했을지 몰라도 논리적으로 엔티티와 아주 강한 의존관계를 가지고 있다. 

     

    요약

    • 진정한 의미의 계층 분할이 어렵다.
    • 엔티티를 신뢰할 수 없다.
    • SQL에 의존적인 개발을 피하기 어렵다.

     

    출처 - [자바 ORM 표준 JPA 프로그래밍 - 저, 김영한]

    http://www.acornpub.co.kr/book/jpa-programmig\

     

    자바 ORM 표준 JPA 프로그래밍

    JPA 기초 이론과 핵심 원리, 그리고 실무에 필요한 성능 최적화 방법까지 JPA에 대한 모든 것

    www.acornpub.co.kr

     

    'FrameWork > ORM' 카테고리의 다른 글

    ORM - JPA설정  (1) 2024.02.14
    ORM - JPA란 무엇인가?  (0) 2024.01.22
    ORM - ORM과 문제 해결3 (객체 그래프 탐색, 비교)  (1) 2024.01.22
    ORM - ORM과 문제 해결2(연관관계)  (1) 2024.01.22
    ORM - ORM과 문제 해결1(상속)  (0) 2024.01.22

    댓글

Designed by Tistory.