Spring을 3 Layer Architecture로 역할 분리하기

2024. 8. 6. 14:37·Spring
목차
  1. Spring 프로젝트를 3 Layer Architecture로 역할 분리하기
  2. Controller에서 Service 분리하기
  3. Service에서 Repository 분리하기
  4. 객체 중복 생성 문제 해결
728x90
반응형

👇🏻 이전 글

2024.08.05 - [Spring] - Spring과 MySQL로 CRUD 기능이 있는 메모장 만들기

 

Spring 프로젝트를 3 Layer Architecture로 역할 분리하기

 

3 Layer Architecture 란 처리 과정을 크게 Controller, Service, Repository 로 나누는 것을 뜻함.

  • Controller : 클라이언트에서 온 요청을 받아 Service로 넘겨주고 처리 완료된 결과를 클라이언트에게 보여주는 역할
  • Service : 사용자의 요구 사항을 처리. 비즈니스 로직 구현하는 곳. DB 저장 및 조회가 필요한 경우 Repository에 요청.
  • Repository : DB 관리. CRUD 작업 처리.

 


 

Controller에서 Service 분리하기

 

Controller는 클라이언트에게서 받은 요청을 그에 알맞는 Service로 넘겨주는 역할이므로 Controller에서 구현된 코드 대부분을 Serviec로 옮기게 된다.

 

MemoService.java

public class MemoService {

    private final JdbcTemplate jdbcTemplate;

    public MemoService(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    public MemoResponseDto createMemo(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;
    }
}

 

  1. 먼저 Service 패키지를 만들고 안에 MemoService 클래스를 만든다.
  2. MemoService에서도 JDBCtemplate를 사용해야 하기 때문에 Controller와 동일하게 JDBCtemplate를 받는 생성자를 만들어준다.
  3. Service안에 구현될 메소드는 Controller와 동일한 이름과 파라미터를 받는 createMemo로 만든다.
  4. Controller의 createMemo의 반환 타입이 MemoResponseDto 였기 때문에 Service의 메소드 반환 타입 또한 동일하게 해준다.

 

MemoController.java

public class MemoController {

    private final JdbcTemplate jdbcTemplate;

    public MemoController(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    @PostMapping("/memos")
    public MemoResponseDto createMemo(@RequestBody MemoRequestDto requestDto) {
        MemoService memoService = new MemoService(jdbcTemplate);
        return memoService.createMemo(requestDto);
    }
}

 

위와 같은 방법으로 Controller에서 Service를 분리해주면 Controller에는 간결한 코드만이 남게 된다.

 


 

Service에서 Repository 분리하기

 

DB 연결 및 CRUD 작업은 Repository에서 이루어져야 하므로 Service에서 Repository 부분을 분리한다.

 

MemoRepository.java

public class MemoRepository {

    private final JdbcTemplate jdbcTemplate;

    public MemoRepository(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    public Memo save(Memo memo) {
        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);

        return memo;
    }
}

 

  1. Repository 패키지를 만든 후 안에 MemoRepository 클래스를 만든다.
  2. DB와 연결되는 곳이기 때문에 JDBCtemplate를 사용할 수 있도록 생성자를 구현한다.
  3. Memo 객체를 파라미터로 받는 save라는 이름의 메소드를 선언한다.
  4. MemoService에서는 Controller에서 받아온 requestDto를 자바 객체로 변환해주는 작업을 마친 뒤 MemoRepository로 넘겨주게 된다.
  5. MemoRepository는 넘겨받은 객체로 나머지 DB 작업을 하게 된다.

 

MemoService.java

public class MemoService {

    private final JdbcTemplate jdbcTemplate;

    public MemoService(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    public MemoResponseDto createMemo(MemoRequestDto requestDto) {
        // RequestDto -> Entity
        Memo memo = new Memo(requestDto);

        // DB 저장
        MemoRepository memoRepository = new MemoRepository(jdbcTemplate);
        Memo saveMemo = memoRepository.save(memo);

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

        return memoResponseDto;
    }
}

 

MemoService는 반환 값으로 MemoResponseDto를 뱉어야 하기 때문에 Repository를 통해 DB에 저장하고 나면 사용한 memo 객체를 responseDto로 변환해서 반환한다.

 


 

조회하기, 수정하기, 삭제하기 기능 또한 위와 같은 방법으로 3계층에 나눠 분리 할 수 있다.

다만 수정하기와 삭제하기에서 사용하는 메소드 findById( )는 DB 조회 메소드이기 때문에 Repository에 적어주게 되는데 이를 Service에서 사용하기 위해 접근제어자를 private → public으로 변경해줘야 한다.

 

수정하기 기능 예시 >

public Long updateMemo(Long id, MemoRequestDto requestDto) {

        MemoRepository memoRepository = new MemoRepository(jdbcTemplate);

        // 해당 메모가 DB에 존재하는지 확인
        Memo memo = memoRepository.findById(id);

        if(memo != null) {
            // memo 내용 수정
            memoRepository.update(id, requestDto);

            return id;
        } else {
            throw new IllegalArgumentException("선택한 메모는 존재하지 않습니다.");
        }
    }

 


 

객체 중복 생성 문제 해결

 

Controller와 Service 코드를 보면 JDBCtemplate를 넘겨주는 코드 한 줄이 메소드 마다 반복되어 적혀 있는 것을 확인 할 수 있다. 이는 생성자 주입으로 다음과 같이 해결 할 수 있다.

 

MemoController.java

    private final MemoService memoService;

    public MemoController(JdbcTemplate jdbcTemplate) {
        this.memoService = new MemoService(jdbcTemplate);
    }

 

위와 같이 적으면 MemoController를 생성할 때 생성자를 통해 자동으로 MemoService에 JDBCtemplate을 넘겨주게 되고 MemoService 선언도 한번만 하면 된다.

같은 방법으로 Service에서 Repository로 JDBCtemplate 넘겨주는 부분도 변경해준다.

 


 

TIL 💭

더보기

이렇게 역할이나 용도에 따라 코드를 나누는 것은 프로젝트를 한 눈에 파악하기도 쉽고 코드를 작성하거나 수정 할 때 등 모든 면에서 편리한 것 같다.

728x90
반응형
저작자표시 비영리 변경금지 (새창열림)

'Spring' 카테고리의 다른 글

Spring의 Filter 이해하기 및 구현하기  (0) 2024.08.16
Spring에서 JWT 이해하기 + 쿠키와 세션 개념까지  (0) 2024.08.16
Spring의 JPA와 Entity 이해하기  (0) 2024.08.07
Spring의 IoC(제어의 역전), DI(의존성 주입) 이해하기  (0) 2024.08.06
Spring과 MySQL로 CRUD 기능이 있는 메모장 만들기  (0) 2024.08.05
  1. Spring 프로젝트를 3 Layer Architecture로 역할 분리하기
  2. Controller에서 Service 분리하기
  3. Service에서 Repository 분리하기
  4. 객체 중복 생성 문제 해결
'Spring' 카테고리의 다른 글
  • Spring에서 JWT 이해하기 + 쿠키와 세션 개념까지
  • Spring의 JPA와 Entity 이해하기
  • Spring의 IoC(제어의 역전), DI(의존성 주입) 이해하기
  • Spring과 MySQL로 CRUD 기능이 있는 메모장 만들기
waVwe
waVwe
    반응형
  • waVwe
    waVwe 개발 블로그
    waVwe
  • 전체
    오늘
    어제
    • ALL (184)
      • Python (1)
      • Spring (15)
      • DevOps (10)
      • Git (6)
      • JAVA (4)
      • C (22)
      • 코테 문제 풀이 (124)
        • 프로그래머스 (43)
        • 백준 (2)
        • 정올 (64)
        • SW Expert Academy (1)
        • 온코더 oncoder (14)
  • 블로그 메뉴

    • 홈
    • 방명록
  • 링크

    • 🐙 Github
  • 공지사항

  • 인기 글

  • 태그

    docker
    이진트리
    C
    온코더
    스파르타코딩
    내일배움캠프
    코테
    도커
    CI/CD
    while문
    스프링
    스프링부트
    MSA
    정올
    devops
    C언어
    깃
    프로그래머스
    깃헙
    연결리스트
    자바
    progate
    아파치카프카
    springboot
    java
    자료구조
    형변환
    알고리즘
    Til
    스파르타코딩클럽
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.0
waVwe
Spring을 3 Layer Architecture로 역할 분리하기
상단으로

티스토리툴바

단축키

내 블로그

내 블로그 - 관리자 홈 전환
Q
Q
새 글 쓰기
W
W

블로그 게시글

글 수정 (권한 있는 경우)
E
E
댓글 영역으로 이동
C
C

모든 영역

이 페이지의 URL 복사
S
S
맨 위로 이동
T
T
티스토리 홈 이동
H
H
단축키 안내
Shift + /
⇧ + /

* 단축키는 한글/영문 대소문자로 이용 가능하며, 티스토리 기본 도메인에서만 동작합니다.