UserController 기능 분리

goose_bumps·2024년 6월 15일

지금까지 여러 API를 만들었는데 한 가지 문제점이 있다.
UserController에만 너무 많은 함수를 만든 점이다.
지금이야 코드 몇 줄이지만 실제로 협업을 하게 되면 함수를 몇 백개는 만들어야 하는데 모든 기능을 한 개의 클래스에 모두 포함시키면 가독성이 너무 떨어진다.
때문에 클린 코드가 필요한 것이다.

기존의 UserController를 Controller -> Service -> Repository 3단 분리를 시키겠다.

1. 기능 분리

CRUD API를 Controller -> Service -> Repository 순으로 분리시키겠다.

1) 유저 데이터 생성 API

    //UserController
    @PostMapping("/user")
    public void saveUser(@RequestBody UserCreateRequest request){
        userService.saveUser(jdbcTemplate, request);
    }
    
    //UserService
    public void saveUser(JdbcTemplate jdbcTemplate, UserCreateRequest request){
        userRepository.saveUser(jdbcTemplate, request.getName(), request.getAge());
    }
    
    //UserRepository
    public void saveUser(JdbcTemplate jdbcTemplate,String name, int age){
        String sql = "insert into user(name,age) value(?,?)";
        jdbcTemplate.update(sql,name,age);
    }

UserController 클래스와 UserService 클래스에는 각각 UserService, UserRepository의 인스턴스를 생성해야 한다.

2) 유저 조회 API

	//UserController
    @GetMapping("/user")
    public List<UserResponse> getUser(){
        return userService.getUser(jdbcTemplate);
    }
    
    //UserService
    public List<UserResponse> getUser(JdbcTemplate jdbcTemplate){
        return userRepository.getUserResponse(jdbcTemplate);
    }
    
    //UserRepository
    public List<UserResponse> getUserResponse(JdbcTemplate jdbcTemplate){
        List<UserResponse> responses = new ArrayList<>();
        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);
            }
        });
    }

3) 유저 업데이트 API

	//UserController
    @PutMapping("/user")
    public void updateUser(@RequestBody UserUpdateRequest request){
        userService.updateUser(jdbcTemplate,request);
    }
    
    //UserService
    public void updateUser(JdbcTemplate jdbcTemplate, UserUpdateRequest request){
        if(userRepository.isUserNotExist(jdbcTemplate,request)){
            throw new IllegalArgumentException();
        }
        userRepository.updateUserName(jdbcTemplate, request.getName(), request.getId());
    }
    
    //UserRepository
    public boolean isUserNotExist(JdbcTemplate jdbcTemplate, UserUpdateRequest request){
        String readsql = "select*from user where id = ?";
        return jdbcTemplate.query(readsql,(rs,rowNum)->0,request.getId()).isEmpty();
    }
    public void updateUserName(JdbcTemplate jdbcTemplate,String name, long id){
        String updatesql = "update user set name = ? where id = ?";
        jdbcTemplate.update(updatesql,name,id);

4) 유저 삭제 API

	//UserController
    @DeleteMapping("/user")
    public void deleteUser(@RequestParam String name){
        userService.deleteUser(jdbcTemplate, name);
    }
    
    //UserService
    public void deleteUser(JdbcTemplate jdbcTemplate, String name){
        if(userRepository.isUserNotExist(jdbcTemplate,name)){
            throw new IllegalArgumentException();
        }
        userRepository.deleteUser(jdbcTemplate,name);
    }
    
    //UserRepository
    public boolean isUserNotExist(JdbcTemplate jdbcTemplate, String name){
        String readsql = "select * from user where name = ?";
        return jdbcTemplate.query(readsql,(rs,rowNum)->0,name).isEmpty();
    }
    public void deleteUser(JdbcTemplate jdbcTemplate, String name){
        String deletesql = "delete from user where name = ?";
        jdbcTemplate.update(deletesql,name);
    }

기능을 분리한다고 해서 특별히 다른 건 없고 기존에 있던 코드를 3개의 클래스로 분담해서 작성하는 것이다.
UserController는 UserServie로, UserService는 UserRepository로 기능을 짬 때리는 것이다.

따라서, 상위 단계로 갈수록 단순 호출만 코드로 적으면 훨씬 가독성이 좋아진다.

2. JdbcTemplate 제거

UserRepository부터 UserController까지 일일히 JdbcTemplate의 파라미터를 통해 전달해야 하는 번거로움이 있다.
좀 더 간결하게 할 수는 없을까?

매개변수가 있는 생성자를 활용하면 해결가능하다.

	//UserRepository
    private final JdbcTemplate jdbcTemplate;
    public UserRepository(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }
    
    //UserService
    UserRepository userRepository;
    public UserService(JdbcTemplate jdbcTemplate){
        this.userRepository = new UserRepository(jdbcTemplate);
    }
    
    //UserController
    private final JdbcTemplate jdbcTemplate;
    private final UserService userService;
    public UserController(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
        this.userService = new UserService(jdbcTemplate);
    }

생성자를 통해 JdbcTemplate을 넘겨받았기 때문에 메서드에는 인자로 넘길 필요가 없어진다. UserRepository 클래스 자체가 JdbcTemplate을 가지고 있고 생성자를 통해 UserService와 UserController에 넘겨주었기 때문에 JdbcTemplate 인자를 없애도 된다.

그럼 다음과 같이 코드가 간결해진다.

	//UserController
    @PostMapping("/user") //사용자 등록
    public void saveUser(@RequestBody UserCreateRequest request){
        userService.saveUser(request);
    }

    @GetMapping("/user")
    public List<UserResponse> getUser(){
        return userService.getUser();
    }

    @PutMapping("/user")
    public void updateUser(@RequestBody UserUpdateRequest request){
        userService.updateUser(request);
    }

    @DeleteMapping("/user")
    public void deleteUser(@RequestParam String name){
        userService.deleteUser(name);
    }
    
    //UserService
    public void saveUser(UserCreateRequest request){
        userRepository.saveUser(request.getName(), request.getAge());
    }

    public List<UserResponse> getUser(){
        return userRepository.getUserResponse();
    }

    public void updateUser(UserUpdateRequest request){
        if(userRepository.isUserNotExist(request)){
            throw new IllegalArgumentException();
        }
        userRepository.updateUserName(request.getName(), request.getId());
    }

    public void deleteUser(String name){
        if(userRepository.isUserNotExist(name)){
            throw new IllegalArgumentException();
        }
        userRepository.deleteUser(name);
    }
    
    //UserRepository
    public void saveUser(String name, int age){
        String sql = "insert into user(name,age) value(?,?)";
        jdbcTemplate.update(sql,name,age);
    }

    public List<UserResponse> getUserResponse(){
        List<UserResponse> responses = new ArrayList<>();
        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);
            }
        });
    }

    public boolean isUserNotExist(UserUpdateRequest request){
        String readsql = "select*from user where id = ?";
        return jdbcTemplate.query(readsql,(rs,rowNum)->0,request.getId()).isEmpty();
    }
    public void updateUserName(String name, long id){
        String updatesql = "update user set name = ? where id = ?";
        jdbcTemplate.update(updatesql,name,id);
    }
    public boolean isUserNotExist(String name){
        String readsql = "select * from user where name = ?";
        return jdbcTemplate.query(readsql,(rs,rowNum)->0,name).isEmpty();
    }
    public void deleteUser(String name){
        String deletesql = "delete from user where name = ?";
        jdbcTemplate.update(deletesql,name);
    }

JdbcTemplate 매개변수가 없어져 훨씬 가독성이 좋아진 것을 알 수 있다.

3. PostMan으로 확인

이제 PostMan으로 수정된 API가 잘 작동하는지 확인해보자.

CRUD 순서대로 데이터 저장 먼저 해보자.

PostMapping API가 정상적으로 작동하여 200 OK가 출력되었다.

데이터가 잘 저장되었나 확인하기 위해 전체 데이터를 조회해보겠다.

정상적으로 조회되었다.

다음은 데이터를 업데이트 해보자.

name이 Park인 데이터를 Lee로 변경하겠다.

데이터 업데이트가 성공하여 200 OK가 출력되었다.

전체 데이터를 조회해보니 데이터 업데이트가 잘 된 것을 확인할 수 있다.

마지막으로 방금 수정한 데이터를 삭제해보자.

데이터 삭제 성공!

전체 데이터 조회 결과, name이 Lee였던 데이터가 삭제되었다.


0개의 댓글