26/07/21 IL(I Learned) - Step 2

MyBatis Mapper와 Service 계층의 역할 이해

가장 크게 달라진 부분은 Repository 대신 MyBatis Mapper를 사용하여 데이터베이스와 통신한다는 점이다.

이전 프로젝트에서는 JdbcTemplate을 이용하여 Repository 안에서 SQL을 직접 실행하였다. 반면 이번 프로젝트에서는 Mapper 인터페이스만 작성하고 SQL은 XML에서 관리하도록 구조가 변경되었다.

또한 Service 계층에서는 단순히 Mapper를 호출하는 것이 아니라 DTO와 Entity를 변환하고 필요한 비즈니스 로직을 수행하는 역할을 담당하였다.

이번 파트에서는 Mapper와 Service가 실제로 어떻게 동작하는지를 중심으로 정리하였다.


BoardMapper

📚 이전 프로젝트와 달라진 점

Spring JDBCMyBatis
Repository 클래스Mapper Interface
JdbcTemplate 사용Mapper 메서드 호출
SQL을 Java에서 작성SQL을 XML에서 작성
RowMapper 필요자동 매핑

Repository 클래스가 없어지고 Mapper 인터페이스가 데이터 접근을 담당하게 되었다.


Mapper Interface

@Mapper
public interface BoardMapper {

    int insert(Board board);

    List<Board> findAll();

    Board findById(long id);

    int update(Board board);

    int delete(long id);
}

Mapper에는 SQL이 존재하지 않는다.

메서드만 선언하면 MyBatis가 XML에 작성된 SQL과 연결하여 실행한다.


MyBatis 내부 동작

BoardService

↓

BoardMapper.insert()

↓

MyBatis

↓

BoardMapper.xml

↓

INSERT SQL 실행

↓

Database

즉, 우리가 호출하는 것은 인터페이스의 메서드이지만 실제 실행은 XML에 작성된 SQL이 담당한다.

Spring은 실행 시 Mapper 인터페이스의 구현 객체(Proxy)를 자동으로 생성하여 주입한다.

따라서 개발자는 구현 클래스를 직접 만들 필요가 없다.


BoardService

Service의 역할

이번 프로젝트에서도 Service의 역할은 이전 프로젝트와 동일하다.

  • Controller와 Mapper 연결
  • 비즈니스 로직 수행
  • DTO ↔ Entity 변환

하지만 Repository 대신 Mapper를 호출한다는 점이 달라졌다.


게시글 생성

public void create(BoardFormDTO dto) {
    boardMapper.insert(dto.toEntity());
}

Service에서는

BoardFormDTO

↓

toEntity()

↓

Board Entity

↓

BoardMapper.insert()

순서로 처리된다.

DTO 내부의 toEntity()를 이용하여 Entity를 만든 뒤 Mapper에 전달한다.


게시글 조회

조회에서는 조금 다른 흐름을 사용한다.

return boardMapper.findAll()
        .stream()
        .map(BoardViewDTO::fromEntity)
        .toList();

Stream API 활용

조회 결과는

List<Board>

이다.

하지만 화면에서는

List<BoardViewDTO>

가 필요하다.

그래서 Stream API를 이용하여

List<Board>

↓

stream()

↓

BoardViewDTO.fromEntity()

↓

List<BoardViewDTO>

로 변환한다.

기존 프로젝트에서는 반복문을 사용할 수도 있었지만, Stream API를 사용하면 객체 변환을 더 간결하게 작성할 수 있다.


게시글 수정

수정 과정은 다음과 같다.

게시글 조회

↓

Entity 수정

↓

Mapper.update()

코드를 보면

Board board = boardMapper.findById(id);

board.setTitle(dto.title());

boardMapper.update(board);

기존 데이터를 조회한 뒤 필요한 값만 변경하여 다시 저장하는 방식이다.


MemberMapper

MemberMapper에서 흥미로운 점은

List<MemberViewDTO> findAll();

이다.

BoardMapper는 Entity를 반환하지만,

MemberMapper는 처음부터 DTO를 반환한다.

즉,

Database

↓

MyBatis

↓

MemberViewDTO

로 바로 변환된다.

필드명이 동일하면 MyBatis가 자동으로 DTO에 값을 매핑할 수 있기 때문에 가능한 방식이다.


MemberService

MemberService 역시 구조는 동일하다.

memberMapper.insert(dto.toEntity());

입력 DTO를 Entity로 변환한 뒤 Mapper를 호출한다.

조회는

return memberMapper.findAll();

한 줄로 처리된다.

MemberMapper가 DTO를 바로 반환하기 때문에 Service에서 별도의 변환 과정이 필요하지 않다.

이 부분은 BoardService와 비교했을 때 가장 큰 차이점이다.


Board와 Member의 차이

BoardMember
Entity 반환 후 DTO 변환DTO를 바로 반환
Stream API 사용변환 과정 없음
fromEntity() 호출자동 매핑 활용

같은 MyBatis를 사용하더라도 설계 방식에 따라 구현이 달라질 수 있다는 점을 확인하였다.


이번 프로젝트에서 새롭게 이해한 점

학습 내용이해한 점
MapperRepository 없이도 데이터 접근 계층을 구성할 수 있다.
Proxy 객체MyBatis가 Mapper 인터페이스의 구현 객체를 자동 생성한다.
XML 매핑SQL을 Java 코드와 분리하여 관리할 수 있다.
Stream APIEntity 목록을 DTO 목록으로 간결하게 변환할 수 있다.
DTO 직접 반환상황에 따라 Mapper가 DTO를 바로 반환하도록 설계할 수도 있다.

느낀 점

이번 프로젝트를 통해 MyBatis는 단순히 SQL을 실행하는 라이브러리가 아니라, Mapper 인터페이스와 XML을 연결하여 데이터 접근을 추상화하는 프레임워크라는 점을 이해하게 되었다. 또한 Board와 Member의 구현 방식을 비교하면서 항상 하나의 방식만 사용하는 것이 아니라, 상황에 따라 Entity를 반환한 뒤 Service에서 DTO로 변환할지, Mapper에서 DTO를 직접 반환할지 선택할 수 있다는 점도 새롭게 배울 수 있었다.

profile
예 마 함 가보입시더

0개의 댓글