◇ PART · MVC

DAO / Repository

SQL 만 책임지는 계층

학습 목표

  • DAO/Repository 의 단 하나의 책임 을 안다
  • DAO 와 Repository 의 차이 (사실상 같다)
  • Service 가 직접 DB 를 호출하지 않는 이유를 안다
  • @Repository 어노테이션의 효과를 안다

⚠️ Service 가 DB 를 직접 다룬다면

계층 분리 안 했을 때

@Service
public class BoardService {
    public Board find(int num) {
        // SQL 직접 작성, JDBC 직접 사용
        Connection c = dataSource.getConnection();
        PreparedStatement ps = c.prepareStatement(
            "SELECT num, title, writer, content FROM myboard WHERE num=?");
        // ... 30 줄
    }
}

Service 가 비즈니스 로직 + DB 접근을 모두 책임 → 단위 테스트 불가능, 변경 영향 큼.

🛠️ DAO/Repository — 창고 관리자

DB 접근만 책임

식당의 「식재료 창고 관리자」. 셰프(Service)는 「김치 한 통 가져와」 라고만 부탁. 어떻게 가져오는지(SQL)는 창고 관리자의 일.


package com.smhrd.mapper;

@Mapper
public interface BoardMapper {
    Board       selectOne(int num);
    List<Board> selectList();
    void        insert(Board b);
    void        update(Board b);
    void        delete(int num);
}

👉 메서드 시그니처만 — 비즈니스 로직 없음.

DAO 와 Repository — 같은 일

이름출처본 과정에서
DAO전통적 패턴 (Data Access Object)같은 의미로 사용 가능
RepositoryDDD 의 개념. Spring 권장@Repository 어노테이션
@MapperMyBatis 의 어노테이션본 과정 사용 — Mapper 인터페이스 자동 등록

👉 본 과정은 「Mapper」 라는 단어를 우선 사용. DAO·Repository 는 동의어.

@Repository 어노테이션 (참고)


@Repository
public class JdbcBoardDao {
    @Autowired private JdbcTemplate jdbc;

    public Board selectOne(int num) {
        return jdbc.queryForObject(
            "SELECT num, title, writer, content FROM myboard WHERE num = ?",
            new BeanPropertyRowMapper<>(Board.class), num
        );
    }
}

@Repository 의 효과:

  • Spring 컨테이너 자동 등록 (@Component 와 같음)
  • SQLException → DataAccessException 자동 변환
  • 의미적 표시 — 「이 클래스는 DB 접근 계층」

본 과정의 선택 — @Mapper


package com.smhrd.mapper;

@Mapper                          // ⭐ MyBatis 가 자동 등록
public interface BoardMapper {
    List<Board> selectList();
}

<!-- com/smhrd/mapper/BoardMapper.xml -->
<mapper namespace="com.smhrd.mapper.BoardMapper">
    <select id="selectList" resultType="com.smhrd.domain.Board">
        SELECT num, title, writer, content
        FROM myboard ORDER BY num DESC
    </select>
</mapper>

👉 본 과정은 인터페이스 + XML 조합. 구현 클래스 직접 안 만듦. MyBatis 가 자동 구현.

DAO/Repository 가 안 하는 일

금기
  • 비즈니스 로직 — 재고 확인 · 할인 계산 등은 Service
  • 예외 처리 정책 — 「재고 부족」 같은 도메인 예외는 Service
  • 여러 테이블 조합 로직 — JOIN 한 번은 OK. 「주문 + 결제 + 알림」 같은 조합은 Service
  • 외부 시스템 호출 — Service 의 일

👉 「SQL 한 번에 하는 일」 까지가 DAO 의 책임.

Service ↔ Mapper 의존


@Service
public class BoardService {

    @Autowired
    private BoardMapper mapper;        // ⭐ 의존 방향: Service → Mapper

    public Board find(int num) {
        return mapper.selectOne(num);
    }
}
✓ Service 가 Mapper 를 호출 ✗ Mapper 가 Service 를 호출 (절대 X) ✗ Mapper 가 Controller 를 호출 (절대 X)

여러 Mapper 활용


@Service
public class OrderService {
    @Autowired private OrderMapper orderMapper;
    @Autowired private ItemMapper itemMapper;
    @Autowired private PaymentMapper paymentMapper;

    @Transactional
    public void order(...) {
        Item i = itemMapper.findById(itemId);    // ① 재고
        orderMapper.insert(order);                // ② 주문
        itemMapper.decreaseStock(itemId, qty);    // ③ 차감
        paymentMapper.insert(payment);            // ④ 결제 기록
    }
}

👉 여러 Mapper 를 「조합」 하는 일이 Service 의 핵심. 한 비즈니스 트랜잭션 단위로 묶임.

패키지 구조

src/main/java/com/smhrd/ ├─ controller/ │ └─ BoardController.java ├─ service/ │ └─ BoardService.java ├─ mapper/ ← DAO/Repository 가 사는 곳 │ ├─ BoardMapper.java ← 인터페이스 │ └─ MemberMapper.java └─ domain/ ├─ Board.java └─ Member.java src/main/resources/com/smhrd/mapper/ ├─ BoardMapper.xml ← SQL └─ MemberMapper.xml

실수 모음

실수증상
@Mapper 빠뜨림"No qualifying bean of type 'BoardMapper'"
Service 에 SQL 직접 작성책임 분리 깨짐
Mapper 에서 비즈니스 로직같은 로직이 여러 Mapper 에 중복
Mapper 가 다른 Mapper 호출의존 관계 복잡. Service 거쳐야 함

🔄 Before / After

전 차시 끝

Service 와 Mapper 가 따로 있다는 정도만 안다.

이번 차시 끝

Mapper 의 단 하나의 책임 = SQL. 비즈니스 로직은 안 들어가는 이유를 안다.

이번 차시의 데이터 흐름

Service
Mapper 인터페이스
Mapper XML
DB
Mapper 두 박스가 흐름의 끝부분에서 SQL 만 책임

정리

오늘 들고 가는 것

  • DAO = Repository = Mapper (본 과정에서) — 같은 책임
  • 「SQL 한 번에 하는 일」 까지가 DAO 의 책임
  • 비즈니스 로직 · 예외 정책 · 외부 호출 = 모두 Service
  • 의존 방향: Service → Mapper (역방향 X)