Spring Boot 강좌 7 : 3 Layer Architecture

coldrice99·2024년 10월 7일
0
post-thumbnail

오늘은 Spring의 3 Layer Architecture에 대해 학습하였다. 기존 메모장 프로젝트의 구조적 문제점을 개선하기 위해 Controller, Service, Repository 간의 역할을 분리하고, 각 레이어의 역할을 명확히 구분하는 방법을 적용하였다.


1. 3 Layer Architecture란?

기존 메모장 프로젝트는 모든 API를 MemoController 클래스에서 처리하고 있었다. 이는 현재는 단순해 보이지만, 기능이 추가되고 복잡해질수록 여러 문제가 발생할 수 있다:

  • 코드가 비대해져 가독성이 떨어진다.
  • 유지보수가 어렵다.
  • 기능 추가 및 변경 시, 코드의 다른 부분과 충돌할 가능성이 높다.

이를 해결하기 위해 3 Layer Architecture를 도입하였다. 이 구조는 크게 Controller, Service, Repository 세 개의 레이어로 나뉘며, 각자의 역할을 명확히 분리함으로써 코드의 유지보수성과 확장성을 높인다.

각 레이어의 역할

  • Controller: 클라이언트의 요청을 받아 적절한 데이터를 Service에 전달하고, 처리 결과를 다시 클라이언트에게 응답한다.
  • Service: 비즈니스 로직을 처리하는 핵심 레이어로, 데이터를 저장하거나 조회할 때 Repository에 요청한다.
  • Repository: 데이터베이스와의 상호작용을 담당하며, CRUD(생성, 조회, 수정, 삭제) 작업을 수행한다.

이 아키텍처를 도입함으로써, 코드가 모듈화되고 유지보수가 더 쉬워진다.


2. 코드 분리: Controller, Service, Repository

(1) Controller - Service 분리

MemoController는 클라이언트로부터 요청을 받아 그 데이터를 MemoService로 전달하고, 처리된 결과를 응답하는 역할만을 담당한다.

@RestController
@RequestMapping("/api")
public class MemoController {

    private final MemoService memoService;

    // MemoService를 의존성 주입 방식으로 사용
    public MemoController(MemoService memoService) {
        this.memoService = memoService;
    }

    @PostMapping("/memos")
    public MemoResponseDto createMemo(@RequestBody MemoRequestDto requestDto) {
        return memoService.createMemo(requestDto); // Service에 메모 생성 로직을 위임
    }

    @GetMapping("/memos")
    public List<MemoResponseDto> getMemos() {
        return memoService.getMemos(); // Service에 메모 조회 로직을 위임
    }

    @PutMapping("/memos/{id}")
    public Long updateMemo(@PathVariable Long id, @RequestBody MemoRequestDto requestDto) {
        return memoService.updateMemo(id, requestDto); // Service에 메모 수정 로직을 위임
    }

    @DeleteMapping("/memos/{id}")
    public Long deleteMemo(@PathVariable Long id) {
        return memoService.deleteMemo(id); // Service에 메모 삭제 로직을 위임
    }
}

Controller는 비즈니스 로직을 처리하지 않고, 단순히 요청을 MemoService에 넘기는 역할만 수행한다. Service는 실제 비즈니스 로직을 처리하는 곳이다.

(2) Service - Repository 분리

Service는 비즈니스 로직을 처리하며, 데이터베이스와 관련된 작업이 필요할 때는 MemoRepository에 그 작업을 요청한다.

@Service
public class MemoService {

    private final MemoRepository memoRepository;

    public MemoService(MemoRepository memoRepository) {
        this.memoRepository = memoRepository;
    }

    public MemoResponseDto createMemo(MemoRequestDto requestDto) {
        Memo memo = new Memo(requestDto); // RequestDto를 Memo 엔티티로 변환
        Memo savedMemo = memoRepository.save(memo); // DB 저장을 Repository에 위임
        return new MemoResponseDto(savedMemo); // 저장된 결과를 ResponseDto로 변환
    }

    public List<MemoResponseDto> getMemos() {
        return memoRepository.findAll(); // 모든 메모 조회를 Repository에 위임
    }

    public Long updateMemo(Long id, MemoRequestDto requestDto) {
        Memo memo = memoRepository.findById(id); // 메모 조회
        if (memo != null) {
            memo.update(requestDto); // 내용 수정
            memoRepository.update(id, requestDto); // 수정된 내용을 DB에 반영
            return id;
        } else {
            throw new IllegalArgumentException("선택한 메모는 존재하지 않습니다.");
        }
    }

    public Long deleteMemo(Long id) {
        Memo memo = memoRepository.findById(id); // 메모 조회
        if (memo != null) {
            memoRepository.delete(id); // 메모 삭제를 Repository에 위임
            return id;
        } else {
            throw new IllegalArgumentException("선택한 메모는 존재하지 않습니다.");
        }
    }
}

Service는 클라이언트의 요청을 처리하는 비즈니스 로직을 담당하며, 데이터베이스와 상호작용이 필요할 때 Repository를 사용한다. 비즈니스 로직이 깔끔하게 분리되어 가독성이 좋아진다.

(3) Repository 역할

MemoRepository는 데이터베이스와 직접 상호작용하는 부분으로, CRUD 작업을 처리한다. 모든 데이터베이스 관련 로직이 이 레이어에서 수행된다.

@Repository
public class MemoRepository {

    private final JdbcTemplate jdbcTemplate;

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

    public Memo save(Memo memo) {
        String sql = "INSERT INTO memo (username, contents) VALUES (?, ?)";
        jdbcTemplate.update(sql, memo.getUsername(), memo.getContents());
        return memo;
    }

    public List<MemoResponseDto> findAll() {
        String sql = "SELECT * FROM memo";
        return jdbcTemplate.query(sql, (rs, rowNum) -> {
            Long id = rs.getLong("id");
            String username = rs.getString("username");
            String contents = rs.getString("contents");
            return new MemoResponseDto(id, username, contents);
        });
    }

    public void update(Long id, MemoRequestDto requestDto) {
        String sql = "UPDATE memo SET username = ?, contents = ? WHERE id = ?";
        jdbcTemplate.update(sql, requestDto.getUsername(), requestDto.getContents(), id);
    }

    public void delete(Long id) {
        String sql = "DELETE FROM memo WHERE id = ?";
        jdbcTemplate.update(sql, id);
    }

    public Memo findById(Long id) {
        String sql = "SELECT * FROM memo WHERE id = ?";
        return jdbcTemplate.queryForObject(sql, (rs, rowNum) -> {
            Memo memo = new Memo();
            memo.setUsername(rs.getString("username"));
            memo.setContents(rs.getString("contents"));
            return memo;
        }, id);
    }
}

Repository는 데이터베이스와의 CRUD 작업을 수행하며, 이 작업은 모두 Repository에서 처리된다. 따라서 데이터베이스 관련 로직이 한 곳에 집중되어 관리가 편리하다.


3. 3 Layer Architecture 도입의 장점

  • 유지보수 용이: 비즈니스 로직, 데이터베이스 로직, 요청 처리 로직이 각각 분리되어 있어 수정하거나 확장할 때 다른 부분에 영향을 주지 않도록 관리할 수 있다.
  • 가독성 향상: 각 레이어가 맡은 역할이 명확하여 코드의 가독성이 좋아지고, 쉽게 이해할 수 있다.
  • 확장성: 새로운 기능을 추가할 때 각 레이어에서만 작업하면 되기 때문에 코드의 확장성이 높아진다.

마무리

오늘 학습한 3 Layer Architecture를 통해 기존 메모장 프로젝트의 구조적 문제를 개선할 수 있었으며, 앞으로도 이 구조를 사용하여 유지보수성과 가독성을 높이는 프로젝트를 진행할 수 있을 것이다.


https://github.com/coldrice99/Spring-boot-Lecture2.git

profile
서두르지 않으나 쉬지 않고

0개의 댓글