3 Layer Architecture

‍박태우·2024년 9월 30일

Spring

목록 보기
2/6

1. Spring 의 3 Layer Architecture

Controller 하나만으로 클라이언트의 요청을 처리하기에는 Controller 의 코드의 길이가 너무 길어지고 코드의 가독성이 낮아진다는 단점이 있다.

=> 위와 같은 문제를 해결하기 위해 처리과정을 아래와 같은 3개로 나눔
Controller , Service, Repository 로 분리함

1) Controller

  • 클라이언트의 요청을 받는다.
  • 요청에 대한 로직 처리는 Service 에 전달한다.
  • 나중에 처리가 완료되고 나면 완료된 결과를 클라이언트에게 응답한다.

2) Service

  • 사용자의 요구사항을 처리하는 제일 중요한 부분 (비즈니스 로직 처리)
  • 제일 비대해지고 있는 코드
  • DB 저장 및 조회가 필요할 때는 Repository 에게 요청

3) Repository

  • DB와 가장 가까이서 DB를 관리하는 역할을 한다. (연결, 해제, 자원관리)
  • DB CRUD 작업을 처리

=> 정리하면 위와 같다.


2. 계층 분리 전 vs 후

  • 메모를 추가하는 컨트롤러
@PostMapping("/memos")
    public MemoResponseDto createMemo(@RequestBody MemoRequestDto requestDto) {
        // RequestDto -> Entity
        Memo memo = new Memo(requestDto);

        // DB 저장
        KeyHolder keyHolder = new GeneratedKeyHolder(); // 기본 키를 반환받기 위한 객체

        String sql = "INSERT INTO memo (username, contents) VALUES (?, ?)";
        jdbcTemplate.update( con -> {
                    PreparedStatement preparedStatement = con.prepareStatement(sql,
                            Statement.RETURN_GENERATED_KEYS);

                    preparedStatement.setString(1, memo.getUsername());
                    // 첫번째 ? 에 대입할 것

                    preparedStatement.setString(2, memo.getContents());
                    // 두번째 ? 에 대입할 것
                    return preparedStatement;
                },
                keyHolder);

        // DB Insert 후 받아온 기본키 확인
        Long id = keyHolder.getKey().longValue();
        memo.setId(id);

        // Entity -> ResponseDto
        MemoResponseDto memoResponseDto = new MemoResponseDto(memo);

        return memoResponseDto;
    }

기존의 POST 요청이 오면 body의 내용을 가지고 jdbc 를 통해 db에 저장하는 코드이다. 좀 길고 읽기 쉽지 않은편 그러나 동작은 잘 한다.

  • Post 요청을 담당하는 컨트롤러
  // 메모 추가
    @PostMapping("/memos")
    public MemoResponseDto createMemo(@RequestBody MemoRequestDto requestDto) {

        MemoService memoService = new MemoService(jdbcTemplate);
        return memoService.createMemo(requestDto); // 컨트롤러와 서비스의 이름을 일치 시키면 좋다.

    }

위 코드에 비해 정말 간결해진 것을 확인 할 수 있다.

  • 분리된 코드 (Service)
public MemoResponseDto createMemo(MemoRequestDto requestDto) {
        // RequestDto -> Entity
        Memo memo = new Memo(requestDto);

        MemoRepository memoRepository = new MemoRepository(jdbcTemplate);
        
        
        Memo saveMemo =memoRepository.save(memo); // db 저장
        // Entity -> ResponseDto
        MemoResponseDto memoResponseDto = new MemoResponseDto(saveMemo);
        return memoResponseDto;
        
        // 위 세줄을 아래 한줄로 줄일 수 있다. (saveMemo 인스턴스를 안쓸것이라면)
       //  return new MemoResponseDto(memoRepository.save(memo));
    }

컨트롤러에서 분리된 코드의 일부이다. 인자로 받아온 requestDto 를 통해 Memo 엔티티 인스턴스를 생성하고 해당 인스턴스를 db와 연결된 repository 로 일을 위임한다.

  • 분리된 코드 (Repository)
public Memo save(Memo memo) {

        // DB 저장
        KeyHolder keyHolder = new GeneratedKeyHolder(); // 기본 키를 반환받기 위한 객체

        String sql = "INSERT INTO memo (username, contents) VALUES (?, ?)";
        jdbcTemplate.update(con -> {
                    PreparedStatement preparedStatement = con.prepareStatement(sql,
                            Statement.RETURN_GENERATED_KEYS);

                    preparedStatement.setString(1, memo.getUsername());
                    // 첫번째 ? 에 대입할 것

                    preparedStatement.setString(2, memo.getContents());
                    // 두번째 ? 에 대입할 것
                    return preparedStatement;
                },
                keyHolder);

        // DB Insert 후 받아온 기본키 확인
        // 아래 코드가 없어도 정상적으로 저장은 되지만 엔티티에 id 값을 주고 요청의 응답으로 반환하기 위해 사용
        Long id = keyHolder.getKey().longValue();
        memo.setId(id);

        return memo;
    }

Repository 가 길다고 생각하지마라!! 나중에 jpa를 사용한다면 이코드는 거의 사라지다 싶이 한다.

  • 결과

이전과 동일하게 CRUD 동작이 정상적으로 동작한다.

(지금은 이런 분리가 정말 편한건지 알 수 없으나 나중에는 Repository 코드가 JPA 덕분에 훨씬 간결해 질 수 있고 또한 나중에 수정요청이 들어오면 모듈화가 되어 있기 때문에 특정 부분만 수정하면 되는 장점이 존재한다.)

profile
잘 부탁드립니다.

0개의 댓글