본문 바로가기
study

[JSCODE] controller, service, repository 구분하기

by 당코 2023. 3. 18.

Controller

  • 클라이언트의 요청 및 응답을 처리
  • 들어온 요청을 Mapping 해주고, 요청자에게 응답을 전달하는 역할
  • @Controller 어노테이션을 사용하여 작성된 Controller 클래스가 이 계층에 포함됨

 

Service

  • 애플리케이션 비즈니스 로직 처리와 비즈니스와 관련된 적합성 검증
  • Controller와 Repository 사이를 연결하는 역할로 두 계층이 직접적으로 통신하지 않게함
  • @Service 어노테이션을 사용하여 작성된 Service 구현 클래스가 이 계층에 포함됨

 

Repositry

  • 데이터베이스에 접근하여 데이터를 CRUD하는 계층
  • 영구 데이터를 빼내어 객체화 시키며, 영구 저장소에 데이터를 저장, 수정, 삭제하는 계층
  • @Repository 어노테이션을 사용하여 작성된 Repository 구현 클래스가 이 계층에 속함

 

3가지 계층으로 나누어 상품 등록 api와 상품 조회 api을 설계해 보았다.

 

상품 등록 api

//Controller
@PostMapping
    public String addProduct(@RequestBody ProductDto productDto) {
        try {
            Long productId = productService.save(productDto);
            return String.valueOf(productId);
        } catch (Exception e) {
            return e.getMessage();
        }
    }

컨트롤러에서는 @RequestBody로 json 데이터를 DTO에 저장하여 저장이 성공한 경우는 상품의 ID를 보여준다.

만약 이미 동일한 이름의 상품이 있다면 예외 처리 후 실패 이유를 알려준다.

 

//Service
private static Long sequence = 0L;

public Long save(ProductDto productDto) {
        Product newProduct = new Product(sequence++, productDto.getName(), productDto.getPrice());
        validateSameName(newProduct);
        return productRepository.save(newProduct);
    }

private void validateSameName(Product newProduct) {
        List<Product> products = productRepository.findAll();
        for (Product product : products) {
            if (newProduct.getName().equals(product.getName())) {
                throw new IllegalArgumentException("동일한 이름의 상품이 존재합니다.");
            }
        }
    }

 

서비스 계층에서는 컨트롤러로부터 넘어온 상품의 이름이 이미 존재하는지 검증을 한 뒤 repository에 저장한다.

동일한 이름의 상품이 존재하면 컨트롤러로 예외를 던진다.

 

//Repository
private final static List<Product> store = new ArrayList<>();

public Product save(Product product) {
	store.add(product);
	return product;
}

Repository에서는 상품을 리스트에 저장하고 저장된 상품의 ID를 반환한다.

 

상품 조회 api

//Controller
@GetMapping
public String getProduct(@RequestParam String name, @RequestParam String monetaryUnit) {
    try {
        if (monetaryUnit.equals("won")) {
            return productService.findOne(name).toStringToWon();
        } else return productService.findOne(name).toStringToDollar();
    } catch (Exception e) {
        return e.getMessage();
    }
}

컨트롤러에서는 쿼리 파라미터의 상품이름과 화폐단위를 받아 won인 경우는 상품정보를 원단위로 보여주고

dollar인 경우는 상품정보를 달러단위로 보여준다.

없는 상품명일 경우는 예외처리한다.

 

//Service
public Product findOne(String name) {
 	Product findProduct = productRepository.findOne(name);
	validateExistName(findProduct);
	return findProduct;
}

private static void validateExistName(Product findProduct) {
	if (findProduct == null) {
		throw new NullPointerException("존재하지 않는 상품명입니다.");
	}
}

서비스 계층에서는 이름으로 상품을 찾아 찾은 상품의 이름이 존재하지 않는지 검증하고 찾은 상품을 반환한다.

만약 존재하지 않는 상품명일 경우 예외를 던진다.

 

//Repository
public Product findOne(String name) {
	for (Product product : store) {
		if (product.getName().equals(name)) {
			return product;
		}
	}
	return null;
}

repository에서는 상품의 이름으로 상품을 찾아 이름과 일치하는 상품이 없는 경우 null을 리턴한다.

 

배운 내용, 깨달은 점

이론을 아는 것과 실제로 구현하는 것은 다른 영역이라는 것을 깨달았다. 실제로 api를 구현해보면서 사소한 것 하나하나를 고민해보면서 3가지 계층에서 어떻게 데이터들이 주고 받아지는지 느낄 수 있었다.

 

궁금한 점

1. 예외처리를 할 때 DB에서 데이터를 가져와 서비스 계층에서 검증을 한 뒤 throw하고 컨트롤러에서 catch하는 방식으로 설계를 했는데 실제로는 어떤 계층에서 이러한 작업들을 처리하는지 궁금합니다.

2. 서비스 계층에서 검증 메서드를 분리하여 서비스 클래스 안에 같이 넣어놨는데 검증 메서드를 따로 검증 패키지를 만들어서 관리를 해야하는지 궁금합니다.

'study' 카테고리의 다른 글

[JSCODE] 상점 API 추가  (0) 2023.03.29
[JSCODE] Spring Data JPA  (0) 2023.03.24
[JSCODE] Spring Data JPA 세팅하기  (0) 2023.03.21
[JSCODE] Spring Boot 프로젝트 생성하기  (0) 2023.03.14
[JSCODE] API 개발에 꼭 필요한 네트워크  (1) 2023.03.09