[Spring] Controller 분리

정석·2024년 2월 23일

Spring

목록 보기
5/21
post-thumbnail

API 생성까진 잘 됐지만 Controller 안에 있는 API 에서 API 호출 및 서비스 로직 구현 그리고 DB 접속을 한 번에 진행하고 있다. 이러한 코드 복잡성을 해결하려면 Serivce, Repository, Controller 로 3단 분할해야 한다.
Spring 을 처음 공부하며 진행하는 프로젝트이므로 오류가 있을 수 있다.

일단, 회원관리 프로젝트로 아래 4개의 CRUD API 를 생성해보았다.

  • 유저 생성
  • 유저 리스트 조회
  • 유저 이름 수정
  • 유저 삭제

1️⃣ 수정 전 API

  1. 유저 생성 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 로직을 수행한다.

  1. 유저 리스트 조회 API
@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 을 수행했을 때, 나온 값들을 RowMapperUserResponse 에 담아서 return 한다. 이 때 리스트의 값이 여러개 존재하기에 순차적으로 진행하여 각 결과값들이 rs 에 담기고 곧바로 저장하는 방식이다.

  1. 유저 이름 수정
@PutMapping("/user")
public void updateUser(@RequestBody UserUpdateRequest request) {
    String sql = "UPDATE user SET name = ? WHERE id = ?";
    jdbcTemplate.update(sql, request.getName(), request.getId());
}

UserUpdateRequest 객체에 담긴 값을 이용한다.

  1. 유저 삭제
@DeleteMapping("/user")
public void deleteUser(@RequestParam String name) {
    String sql = "DELETE FROM user WHERE name = ?";
    jdbcTemplate.update(sql, name);
}

바로 파라미터로 받은 값을 이용하여 삭제한다.


2️⃣ 역할 분리 후 API

Controller -> API 입구 , API 설계하는 곳
Service -> 프로세스 로직을 관리함
Repository -> DB 와의 접근을 담당

이와 같이 역할을 나누었다.

  1. UserController
@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 로직이 구현되지 않도록 한다.

  1. UserService
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 데이터 접근에 대한 수행 없이 로직 구현만 담당한다.

  1. UserRepository
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 에 접속하여 쿼리문을 수행하는 과정만 진행한다.

전체 과정을 그림으로 보면 아래와 같다.

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

0개의 댓글