Spring 문제 해결

JIWOO YUN·2024년 4월 15일
0

SpringDB

목록 보기
8/11
post-custom-banner

현재 작성한 프로젝트의 문제점을 해결하기 애플리케이션의 구조에대해서 알아두자.


애플리케이션 구조

  • 가장 단순하면서 많이 사용하는 방법은 역할에 따라서 3가지로 나누는 것.

프레젠테이션 계층

  • UI 관련된 처리 담당
  • 웹 요청 과 처리 응답
  • 사용자 요청을 검증
  • 서블릿과 같은 HTTP 같은 웹기술과 스프링 MVC 를 주로 사용함.

서비스 계층

  • 비즈니스 로직 담당
  • 가급적 특정 기술에 의존하지 않으며 순수 자바코드로 작성한다.

데이터 접근 계층

  • 실제 데이터베이스에 접근하는 코드
  • JDBC,JPA,File,Redis 등의 여러가지 기술이 사용된다.

순수한 서비스 계층

  • 가장 중요한 곳은 핵심 비즈니스 로직이 들어있는 서비스 계층
    • 시간이 흘려 UI 와 관련된 부분이 변하고, 데이터 저장 기술을 다른 기술로 변경해도, 비즈니스 로직은 최대한 변경없이 유지되어야함.
위의 조건을 최대한 만족시키기 위해서는 서비스 계층은 특정 기술에 종속적이지 않게 개발해야한다.
  • 기술에 종속적인 부분은 프레젠테이션 계층과 데이터 접근 계층이 가져간다.
    • 프레젠테이션 계층이 UI 와 관련된 기술인 웹, 서블릿 HTTP 와 관련된 부분을 담당해주기 때문에 웹,서블릿,HTTP와 관련된 기술로부터 서비스 계층을 보호해줌.
    • 데이터 접근 계층은 데이터를 저장하고 관리하는 기술을 담당하여 JDBC,JPA와 같은 구체적인 데이터 접근 기술로 부터 서비스 계층을 보호해준다.
      • JDBC를 사용하다 JPA로 변경해도 서비스 계층은 변경하지 않아도 된다.

서비스 계층이 특정 기술에 종속되지 않기 때문에 비즈니스 로직을 유지보수하기도 쉽고, 테스트하기에도 용이하기 때문에 종속적이 않게 가는 방향으로 가야한다.

  • 향후 구현 기술이 변경될 수도 있기 때문에 서비스 계층은 가급적 비즈니스 로직만 구현하고 특정 구현 기술에 직접 의존하지않게 작성해야한다는걸 꼭 잊지말자.

현재까지 작성한 MemberServiceV1 의 경우 문제

  1. SQLException 이라는 JDBC에 의존하는 점
    1. memberRepository에서 올라오는 예외이기 때문에 repository에서 해결하는게 맞다.
  2. MemberRepositoryV1 구체적인 클래스에 직접 의존되는 문제.
    1. MemberRepository인터페이스를 도입하여 service쪽 코드의 변경 없이 다른 구현기술로 손쉽게 바꿀수 있게 변경해야한다.

트랜잭션을 적용한 MemberServiceV2 추가

public class MemberServiceV2 {

    private final DataSource dataSource;

    private final MemberRepositoryV2 memberRepository;

    public void accountTransfer(String fromId, String toId, int money) throws SQLException {
        Connection con = dataSource.getConnection();

        try{
            //트랜잭션 시작을 위해서 자동커밋 끄기.
            con.setAutoCommit(false);
            bizLogic(con,fromId, toId, money);
            con.commit();
        }catch (Exception e){
            con.rollback();
            throw new IllegalStateException(e);
        }finally {
            release(con);
        }


    }

    private void bizLogic(Connection con, String fromId, String toId, int money) throws SQLException {
        Member fromMember = memberRepository.findById(con,fromId);
        Member toMember = memberRepository.findById(con,toId);

        memberRepository.update(con,fromId, fromMember.getMoney() - money);
        validation(toMember);
        memberRepository.update(con,toId, toMember.getMoney() + money);
    }

    private void validation(Member toMember) {
        if (toMember.getMemberId().equals("ex")) {
            throw new IllegalStateException("이체중 예외 발생");
        }
    }

    private void release(Connection con){
        if(con != null){
            try{
                con.setAutoCommit(true);
                con.close();
            }catch (Exception e){
                log.info("error",e);
            }
        }
    }
}
  • 여전히 트랜잭션을 사용하기 위해 JDBC 기술에 의존해야하는 문제가 해결되지 않았음.
    • 트랜잭션을 사용하기 위해서 트랜잭션 처리 코드가 더 많아짐.
  • 핵심 비지니스 로직과 JDBC 기술이 섞여있어서 유지보수가 어려움.

트랜잭션을 적용하고도 계속 있는 문제점 3가지

  1. 트랜잭션 문제

    • JDBC 구현 기술이 서비스 계층에 누수되는 문제
      • 트랜잭션을 적용하기 위해서 JDBC 구현 기술이 서비스 계층이 누수되고 있음.
      • "서비스 계층은 순수해야한다" 의 의미는구현기술을 변경해도 서비스 계층 코드는 최대한 유지해야하는 뜻으로 변화에 잘 대응하게 해줘야함
        • 이 이유 때문에 데이터 접근 계층에 JDBC 코드를 몰아둔다.
        • 데이터 접근 계층의 구현 기술이 변경될 수 있으므로 데이터 접근 계층은 인터페이스를 제공하는게 좋음.
      • 트랜잭션을 적용하면서 결국에는 서비스 계층에 JDBC 구현 기술의 누수가 발생.
  2. 예외 누수 문제

    • 데이터 접근 계층의 JDBC 구현 기술 예외가 서비스 계층으로 전파되고 있음.
      • 서비스 부분에서 SQLException을 throw 하고 있음
        • SQLException 은 JDBC 전용기술 -> 향후 JPA 나 다른 데이터 접근 기술을 사용하게 되면 그에 맞는 예외로 변경을 진행해야하기 때문에 서비스 코드도 수정해야하는 문제 발생.
  3. JDBC 반복 문제

    • MemberRepository코드의 경우 순수한 JDBC 사용중
    • 유사한 코드의 반복성
      • try,catch,finally
      • 커넥션 열고 preparedStateMent 사용, 결과 매핑 -> 실행하고 커넥션과 리소스를 정리 반복

이 문제들을 해결하기 위해서 트랜잭션 추상화를 사용한다.

profile
열심히하자
post-custom-banner

0개의 댓글