Service 작성 가이드

Service 레이어 작성 패턴, 트랜잭션, 비즈니스 로직 구성 규칙

마지막 수정: 2026-05

Service 작성 가이드

기본 원칙

  • Interface 작성 안 함 — Service 클래스 직접 사용
  • @Service, @RequiredArgsConstructor, @Slf4j 필수
  • 쓰기 트랜잭션: @Transactional(rollbackFor = Exception.class)
  • 읽기 트랜잭션: @Transactional(readOnly = true)
  • HTTP 관련 객체 (HttpServletRequest, HttpSession) 직접 사용 금지
  • 하나의 메서드는 하나의 역할만 수행

서비스 클래스 기본 구조

@Service
@RequiredArgsConstructor
@Slf4j
public class UserService {

    private final UserMapper userMapper;

    /**
     * 사용자 단건 조회
     */
    @Transactional(readOnly = true)
    public UserVo get(UserVo vo) {
        return userMapper.get(vo);
    }

    /**
     * 사용자 목록 조회
     */
    @Transactional(readOnly = true)
    public List<UserVo> getList(UserVo vo) {
        return userMapper.getList(vo);
    }

    /**
     * 사용자 전체 수 조회
     */
    @Transactional(readOnly = true)
    public int getTotalCount(UserVo vo) {
        return userMapper.getTotalCount(vo);
    }

    /**
     * 사용자 등록
     */
    @Transactional(rollbackFor = Exception.class)
    public void regist(UserVo vo) {
        userMapper.regist(vo);
    }

    /**
     * 사용자 수정
     */
    @Transactional(rollbackFor = Exception.class)
    public void update(UserVo vo) {
        userMapper.update(vo);
    }

    /**
     * 사용자 삭제
     */
    @Transactional(rollbackFor = Exception.class)
    public void delete(UserVo vo) {
        userMapper.delete(vo);
    }
}

트랜잭션 규칙

메서드 성격트랜잭션 어노테이션
조회 (SELECT)@Transactional(readOnly = true)
쓰기 (INSERT/UPDATE/DELETE)@Transactional(rollbackFor = Exception.class)
// 읽기 전용 트랜잭션
@Transactional(readOnly = true)
public UserVo get(UserVo vo) {
    return userMapper.get(vo);
}

// 쓰기 트랜잭션 — 모든 예외에서 롤백
@Transactional(rollbackFor = Exception.class)
public void regist(UserVo vo) {
    userMapper.regist(vo);
}

복합 트랜잭션 (여러 Mapper 호출)

@Transactional(rollbackFor = Exception.class)
public void registOrder(OrderVo vo) {
    // 주문 등록
    orderMapper.regist(vo);

    // 주문 상세 등록 (같은 트랜잭션)
    for (OrderDetailVo detail : vo.getDetailList()) {
        detail.setOrderSn(vo.getOrderSn());
        orderDetailMapper.regist(detail);
    }

    // 재고 차감
    stockMapper.decrease(vo);
}

비즈니스 검증

Service에서 비즈니스 규칙을 검증한다.

@Transactional(rollbackFor = Exception.class)
public void regist(UserVo vo) {
    // 비즈니스 규칙 검증
    if (userMapper.existsByUserId(vo.getUserId()) > 0) {
        throw new BusinessException("이미 사용 중인 아이디입니다.");
    }

    userMapper.regist(vo);
    log.info("사용자 등록 완료: userId={}", vo.getUserId());
}

로깅

// 주요 비즈니스 이벤트 INFO 로그
log.info("사용자 등록: userId={}", vo.getUserId());

// 경고 상황 WARN 로그
log.warn("사용자 조회 결과 없음: userSn={}", vo.getUserSn());

// 예외 ERROR 로그
log.error("사용자 등록 실패: userId={}", vo.getUserId(), e);

체크리스트

  • [ ] Interface 작성 안 함
  • [ ] @Service, @RequiredArgsConstructor, @Slf4j 선언
  • [ ] 읽기 메서드: @Transactional(readOnly = true)
  • [ ] 쓰기 메서드: @Transactional(rollbackFor = Exception.class)
  • [ ] HTTP 관련 객체 미사용
  • [ ] 비즈니스 검증은 Service 레이어에서 처리