ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 인프런 워밍업 클럽/ BE 6일차 과제 : 스프링 컨테이너의 사용
    인프런 워밍업 클럽 2024. 5. 13. 01:18

     

    6일차 과제

    출처 : 인프런 -  자바와 스프링 부트로 생애 최초 서버 만들기
    Fruit컨트롤러

    기존의 코드를 보면 Controller에서 다 처리해주고 있었다. Controller의 역할인 주소를 연결해주고 요청을 받고 데이터를 내려주고하는 역할을 넘어 비즈니스 로직을 처리하고 DB와 연결까지 하고 있는 것이다.

     

    컨트롤러의 역할과 책임에 맞게 수정을  하게 되면서 컨트롤러는 정확히 요청에서 오는 데이터를 받아주고 요청에 맞는 응답 데이터를 내려주는 역할만 하도록 바꿔줘야한다.


    FruitController

    package com.group.libraryapp.controller.fruit;
    
    import com.group.libraryapp.dto.homework.request.FruitRequestDTO;
    import com.group.libraryapp.dto.homework.request.FruitUpdateRequestDTO;
    import com.group.libraryapp.dto.homework.response.FruitResponseDTO;
    import com.group.libraryapp.service.fruit.FruitService;
    import org.springframework.web.bind.annotation.*;
    
    @RestController
    public class FruitController {
        private final FruitService fruitService;
        public FruitController(FruitService fruitService) {
            this.fruitService = fruitService;
        }
    
        @PostMapping("/api/v1/fruit")
        public void storeFruit(@RequestBody FruitRequestDTO request) {
            fruitService.storeFruit(request);
        }
    
        @PutMapping("/api/v1/fruit")
        public void updateFruit(@RequestBody FruitUpdateRequestDTO request) {
            fruitService.updateFruit(request);
        }
    
        @GetMapping("/api/v1/fruit/stat")
        public FruitResponseDTO getFruitAmount(@RequestParam String name) {
            return fruitService.getFruitAmount(name);
        }
    }

     

    원래 있었던 SQL들이 싹 사라지고 정확히 요청에 대한 데이터를 받고 응답 데이터를 내려주는 역할만 하도록 바뀌었다.


    FruitService

    package com.group.libraryapp.service.fruit;
    
    import com.group.libraryapp.dto.homework.request.FruitRequestDTO;
    import com.group.libraryapp.dto.homework.request.FruitUpdateRequestDTO;
    import com.group.libraryapp.dto.homework.response.FruitResponseDTO;
    import com.group.libraryapp.repository.fruit.FruitRepository;
    import org.springframework.stereotype.Service;
    
    @Service
    public class FruitService {
        private final FruitRepository fruitRepository;
        public FruitService(FruitRepository fruitRepository) {
            this.fruitRepository = fruitRepository;
        }
    
    
        public void storeFruit(FruitRequestDTO request) {
            fruitRepository.storeFruit(request);
        }
    
        public void updateFruit(FruitUpdateRequestDTO request) {
            fruitRepository.updateFruit(request);
        }
    
        public FruitResponseDTO getFruitAmount(String name) {
            int salesAmount = fruitRepository.getSalesAmountByName(name);
            int notSalesAmount = fruitRepository.getNotSalesAmountByName(name);
            return new FruitResponseDTO(salesAmount,notSalesAmount);
        }
    }

    컨트롤러가 요청을 처리한다면 서비스는 비즈니스 로직을 처리하는 역할을 가진다. 현재는 간단한 요청들이기 때문에 단순히 DTO를 리포지토리로 넘겨주는 것 같지만 비즈니스 로직을 처리하는 계층이다.

    getFruitAmount를 보면 리포지토리에 두번의  DB조회 요청을 보낸 것을 합쳐서 ResponseDTO로 만들어 Controller에 반환하는 역할을 하고있다.


    FruitRepository

    package com.group.libraryapp.repository.fruit;
    
    import com.group.libraryapp.dto.homework.request.FruitRequestDTO;
    import com.group.libraryapp.dto.homework.request.FruitUpdateRequestDTO;
    import org.springframework.jdbc.core.JdbcTemplate;
    import org.springframework.stereotype.Repository;
    
    @Repository
    public class FruitRepository {
        private final JdbcTemplate jdbcTemplate;
        public FruitRepository (JdbcTemplate jdbcTemplate) {
            this.jdbcTemplate = jdbcTemplate;
        }
    
        public void storeFruit(FruitRequestDTO request) {
            String sql = "INSERT INTO fruit(name, warehousingDate, price, state) VALUES(?,?,?,0)";
            jdbcTemplate.update(sql,request.getName(), request.getWarehousingDate(), request.getPrice());
        }
    
        public void updateFruit(FruitUpdateRequestDTO request) {
            String sql = "UPDATE fruit SET state=1 WHERE id = ?";
            jdbcTemplate.update(sql,request.getId());
        }
    
    
        public int getSalesAmountByName(String name) {
            String sql = "SELECT sum(price) FROM fruit WHERE state = 1 AND name= ?";
            return jdbcTemplate.queryForObject(sql, new Object[]{name}, Integer.class);
        }
    
        public int getNotSalesAmountByName(String name) {
            String sql = "SELECT sum(price) FROM fruit WHERE state = 0 AND name=?";
            return jdbcTemplate.queryForObject(sql, new Object[]{name}, Integer.class);
        }
    }

    기존 컨트롤러에서 처리하던 SQL문을 리포지토리에서 만들어 DB와 직접적인 연결을 하고 있다.

     

    그리고 스프링 컨테이너를 적극적으로 사용해서 생성자를 통한 의존성 주입을 하고 있다. 때문에 @Service, @Repository 어노테이션을 써서 스프링 서버가 시작될 때 자동으로 스프링 컨테이너에 빈으로 등록이 되고 생성될 때 의존성 주입이 된다.  


    출처 : 인프런 -  자바와 스프링 부트로 생애 최초 서버 만들기

    여러개의 객체를 하나의 타입으로 받아주기 위해서는 인터페이스가 필요하다.

    기존 FruitRepository를 인터페이스를 바꿔주고

    FruitRepository를 implement한 FruitMySqlRepository와

    FruitRepository를 implement한 FruitMemoryRepository 두 개를 만들어 주게 된다면 Repository를 바꿔가면서 동작시킬 수 있게 된다.

     

    단 같은 타입의 Repository를 두 개를 만들게 되면 꼭 둘 중 어느 Repository를 사용할지 정해야만 한다. 그 역할을 @Primary 어노테이션이 하게된다. 

     

    FruitMemoryRepository

    package com.group.libraryapp.repository.fruit;
    
    import com.group.libraryapp.dto.homework.request.FruitRequestDTO;
    import com.group.libraryapp.dto.homework.request.FruitUpdateRequestDTO;
    import org.springframework.context.annotation.Primary;
    import org.springframework.jdbc.core.JdbcTemplate;
    import org.springframework.stereotype.Repository;
    
    @Primary
    @Repository
    public class FruitMemoryRepository implements FruitRepository{
        private final JdbcTemplate jdbcTemplate;
        public FruitMemoryRepository(JdbcTemplate jdbcTemplate) {
            this.jdbcTemplate = jdbcTemplate;
        }
    
        public void storeFruit(FruitRequestDTO request) {
            String sql = "INSERT INTO fruit(name, warehousingDate, price, state) VALUES(?,?,?,0)";
            jdbcTemplate.update(sql,request.getName(), request.getWarehousingDate(), request.getPrice());
        }
    
        public void updateFruit(FruitUpdateRequestDTO request) {
            String sql = "UPDATE fruit SET state=1 WHERE id = ?";
            jdbcTemplate.update(sql,request.getId());
        }
    
    
        public int getSalesAmountByName(String name) {
            String sql = "SELECT sum(price) FROM fruit WHERE state = 1 AND name= ?";
            return jdbcTemplate.queryForObject(sql, new Object[]{name}, Integer.class);
        }
    
        public int getNotSalesAmountByName(String name) {
            String sql = "SELECT sum(price) FROM fruit WHERE state = 0 AND name=?";
            return jdbcTemplate.queryForObject(sql, new Object[]{name}, Integer.class);
        }
    }

    @Primary어노테이션을 붙여줘서 기존에 쓰던 것을 계속 쓰도록 해놨다.

     

    FruitMySqlRepository

    package com.group.libraryapp.repository.fruit;
    
    import com.group.libraryapp.dto.homework.request.FruitRequestDTO;
    import com.group.libraryapp.dto.homework.request.FruitUpdateRequestDTO;
    import org.springframework.stereotype.Repository;
    
    @Repository
    public class FruitMySqlRepository implements FruitRepository{
    
        @Override
        public void storeFruit(FruitRequestDTO request) {
    
        }
    
        @Override
        public void updateFruit(FruitUpdateRequestDTO request) {
    
        }
    
        @Override
        public int getSalesAmountByName(String name) {
            return 0;
        }
    
        @Override
        public int getNotSalesAmountByName(String name) {
            return 0;
        }
    }

    @Primary어노테이션이 없기 때문에 해당 FruitService에 해당 리포지토리가 의존성 주입되지 않게 된다.

    만약 기존의 리포지토리에서 해당  리포지토리로 바꾸고 싶다면  @Primary 어노테이션을 위에 달아주고 기존 리포지토리의 @Primary어노테이션을 지워주면 해당 리포지토리가 의존성이 주입된다.

Designed by Tistory.