프론트엔드를 하다 보면 백엔드 코드가 "왜 이렇게 폴더가 나뉘어 있지?"라는 생각이 들 때가 있습니다. React에서 컴포넌트-훅-API 호출을 분리하듯, 스프링(Spring) 기반 자바 백엔드도 역할별로 계층을 나눕니다. 이번 글에서는 가장 기본이 되는 Controller - Service - Repository 구조와, 데이터베이스에 접근하는 두 가지 방식인 JPA와 MyBatis를 정리해봅니다.
한 파일에 요청 처리, 비즈니스 로직, SQL을 다 몰아넣을 수도 있습니다. 하지만 그렇게 하면:
그래서 역할을 세 계층으로 나눠서, 각 계층이 자기 책임만 지도록 만듭니다.
위 그림처럼 요청은 Client → Controller → Service → Repository → DB 순서로 흘러가고, 응답은 반대 방향으로 돌아옵니다.
Controller는 클라이언트(브라우저, 앱)의 HTTP 요청을 가장 먼저 받는 계층입니다.
@RestController
@RequestMapping("/api/users")
public class UserController {
private final UserService userService;
@GetMapping("/{id}")
public ResponseEntity<UserResponse> getUser(@PathVariable Long id) {
UserResponse user = userService.findUser(id);
return ResponseEntity.ok(user);
}
}
Controller가 하는 일
Controller가 하지 않는 일
프론트엔드로 비유하면, Controller는 라우터(router)에 가깝습니다. "이 URL로 오면 이 로직을 실행해줘"라고 연결만 해주고, 실제 계산은 다른 곳에 위임하는 역할입니다.
Service는 실제 업무 규칙이 담기는 곳입니다. "포인트를 적립한다", "주문을 취소하면 재고를 되돌린다" 같은 로직이 여기서 처리됩니다.
@Service
@RequiredArgsConstructor
public class UserService {
private final UserRepository userRepository;
@Transactional
public UserResponse findUser(Long id) {
User user = userRepository.findById(id)
.orElseThrow(() -> new UserNotFoundException(id));
return UserResponse.from(user);
}
}
Service가 하는 일
Service는 HTTP가 뭔지 몰라도 되고, SQL 문법도 몰라도 됩니다. 오직 "업무가 어떻게 돌아가야 하는지"만 압니다.
Repository는 DB와 직접 대화하는 계층입니다. 여기서 등장하는 것이 JPA와 MyBatis입니다. 둘 다 "자바 객체 ↔ DB 테이블"을 연결해주는 기술이지만, 접근 방식이 다릅니다.
상황 선택하기 좋은 기술
| 신규 서비스, 일반적인 CRUD가 많은 경우 | JPA |
| 복잡한 조회/통계, 세밀한 쿼리 튜닝이 필요한 경우 | MyBatis |
| 금융권 등 레거시 DB·기존 SQL 자산이 많은 경우 | MyBatis (또는 JPA와 혼용) |
실무에서는 둘 중 하나만 쓰기보다, 기본 CRUD는 JPA로, 복잡한 리포트성 쿼리는 MyBatis로 함께 쓰는 경우도 흔합니다.
Client (React/Vue 등)
│ HTTP 요청 (GET /api/users/1)
▼
Controller ← "이 URL 요청이 왔으니 Service한테 넘기자"
│
▼
Service ← "권한 확인하고, 트랜잭션 열고, 로직 처리하자"
│
▼
Repository (JPA/MyBatis) ← "DB에서 실제로 데이터를 가져오자"
│
▼
DB (MariaDB, PostgreSQL...)
각 계층은 자신의 바로 아래 계층만 알면 됩니다. Controller는 Service만 알고, Service는 Repository만 압니다. 덕분에 예를 들어 DB를 MariaDB에서 PostgreSQL로 바꾸더라도, Repository 구현체만 손보면 되고 Controller나 Service 코드는 건드릴 필요가 없어집니다.
Controller-Service-Repository 구조는 결국 "누가 무엇을 몰라도 되게 만들 것인가"를 설계하는 것입니다. 프론트엔드에서 관심사를 분리하듯, 백엔드도 같은 원칙을 계층이라는 형태로 구현한 셈입니다. JPA와 MyBatis는 이 중 Repository 계층에서 선택하는 도구일 뿐, 정답이 정해져 있다기보다는 프로젝트 상황에 맞게 고르는 것이라고 이해하면 됩니다.
| 스프링(Spring)과 스프링 부트(Spring Boot) 뭐가 다른걸까? (0) | 2026.09.04 |
|---|---|
| Java 설치 및 Spring boot 프로젝트 생성 방법 - mac기준 (0) | 2026.09.01 |