API 생성까진 잘 됐지만 Controller 안에 있는 API 에서 API 호출 및 서비스 로직 구현 그리고 DB 접속을 한 번에 진행하고 있다. 이러한 코드 복잡성을 해결하려면 Serivce, Repository, Controller 로 3단 분할해야 한다.
Spring 을 처음 공부하며 진행하는 프로젝트이므로 오류가 있을 수 있다.
일단, 회원관리 프로젝트로 아래 4개의 CRUD API 를 생성해보았다.
@PostMapping("/user")
public void saveUser(@RequestBody UserCreateRequest request) {
String sql = "INSERT INTO user (name, age) VALUES (?, ?)";
jdbcTemplate.update(sql, request.getName(), request.getAge());
}
POST로 UserCreateRequest 객체로 값을 받은 뒤 해당 값을 이용하여 DB 로직을 수행한다.
@GetMapping("/user")
public List<UserResponse> getUsers() {
String sql = "SELECT * FROM user";
return jdbcTemplate.query(sql, new RowMapper<UserResponse>() {
@Override
public UserResponse mapRow(ResultSet rs, int rowNum) throws SQLException {
long id = rs.getLong("id");
String name = rs.getString("name");
int age = rs.getInt("age");
return new UserResponse(id, name, age);
}
});
}
jdbcTemplate 으로 sql 을 수행했을 때, 나온 값들을 RowMapper 로 UserResponse 에 담아서 return 한다. 이 때 리스트의 값이 여러개 존재하기에 순차적으로 진행하여 각 결과값들이 rs 에 담기고 곧바로 저장하는 방식이다.
@PutMapping("/user")
public void updateUser(@RequestBody UserUpdateRequest request) {
String sql = "UPDATE user SET name = ? WHERE id = ?";
jdbcTemplate.update(sql, request.getName(), request.getId());
}
UserUpdateRequest 객체에 담긴 값을 이용한다.
@DeleteMapping("/user")
public void deleteUser(@RequestParam String name) {
String sql = "DELETE FROM user WHERE name = ?";
jdbcTemplate.update(sql, name);
}
바로 파라미터로 받은 값을 이용하여 삭제한다.
Controller -> API 입구 , API 설계하는 곳
Service -> 프로세스 로직을 관리함
Repository -> DB 와의 접근을 담당
이와 같이 역할을 나누었다.
@RestController
public class UserController {
private final UserService userService;
public UserController(JdbcTemplate jdbcTemplate) {
this.userService = new UserService(jdbcTemplate);
}
@PostMapping("/user")
public void saveUser(@RequestBody UserCreateRequest request) {
userService.saveUser(request);
}
@GetMapping("/user")
public List<UserResponse> getUsers() {
return userService.getUsers();
}
@PutMapping("/user")
public void updateUser(@RequestBody UserUpdateRequest request) {
userService.updateUser(request);
}
@DeleteMapping("/user")
public void deleteUser(@RequestParam String name) {
userService.deleteUser(name);
}
}
Controller 에선 API 생성만 수행하며 DB 로직이나 실제 Service 로직이 구현되지 않도록 한다.
public class UserService {
private final UserRepository userRepository;
public UserService(JdbcTemplate jdbcTemplate) {
userRepository = new UserRepository(jdbcTemplate);
}
// 유저 생성
public void saveUser(UserCreateRequest request) {
userRepository.saveUser(request.getName(), request.getAge());
}
// 유저 전체 조회
public List<UserResponse> getUsers() {
return userRepository.getUsers();
}
// 유저 이름 수정
public void updateUser(UserUpdateRequest request) {
if (userRepository.isUserNotExist(request.getId())) {
throw new IllegalArgumentException();
}
userRepository.updateUserName(request.getName(), request.getId());
}
// 유저 삭제
public void deleteUser(String name) {
if (userRepository.isUserNotExist(name)) {
throw new IllegalArgumentException();
}
userRepository.deleteUser(name);
}
}
DB 데이터 접근에 대한 수행 없이 로직 구현만 담당한다.
public class UserRepository {
private final JdbcTemplate jdbcTemplate;
public UserRepository(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
public boolean isUserNotExist(long id) {
String readSql = "SELECT * FROM user WHERE id = ?";
return jdbcTemplate.query(readSql, (rs, rowNum) -> 0, id).isEmpty();
}
public boolean isUserNotExist(String name) {
String readSql = "SELECT * FROM user WHERE name = ?";
return jdbcTemplate.query(readSql, (rs, rowNum) -> 0, name).isEmpty();
}
public void saveUser(String name, int age) {
String sql = "INSERT INTO user (name, age) VALUES (?, ?)";
jdbcTemplate.update(sql, name, age);
}
public List<UserResponse> getUsers() {
String sql = "SELECT * FROM user";
return jdbcTemplate.query(sql, (rs, rowNum) -> { // 쿼리 실행, 근데 값이 여러개이므로 순차진행
long id = rs.getLong("id"); // rs -> 순차적 결과 값
String name = rs.getString("name");
int age = rs.getInt("age");
return new UserResponse(id, name, age);
});
}
public void updateUserName(String name, long id) {
String sql = "UPDATE user SET name = ? WHERE id = ?";
jdbcTemplate.update(sql, name, id);
}
public void deleteUser(String name) {
String sql = "DELETE FROM user WHERE name = ?";
jdbcTemplate.update(sql, name);
}
}
DB 에 접속하여 쿼리문을 수행하는 과정만 진행한다.
전체 과정을 그림으로 보면 아래와 같다.

각 부분을 의존하고 있지만 역할이 분리되어 실행된다.