프로젝트 개요 : 이것이 취업을 위한 백엔드 개발이다 클론코딩 - 상품관리 애플리케이션
프로젝트 환경 : IntelliJ, SpringBoot, MySQL
프로젝트 코드 : https://github.com/smkim9202/ProductManagement
상품 추가
1. 컨트롤러 만들기 : ProductController.java
package kr.co.api.product.management;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ProductController {
@RequestMapping(value = "/products", method= RequestMethod.POST)
public Product createProduct(@RequestBody Product product){
//Product 생성하고 리스트에 넣는 작업 필요
return product;
}
}
/products vs /product
자원의 이름을 단수형으로 할지 복수형으로 할지 구분하기보다는 프로젝트 내에서 일관성 있게 통일해서 사용하는 것이 좋다
커뮤니티에서는 전부 s를 붙여 API를 설계하는 방법이 지배적이다.
2. 상품 getter 코드 추가 : Product.java
- Product 클래스에서 우클릭
- [Generate...](맥북 : command+N)
- [Getter]
- 모든 필드 선택 후 [OK]
접근제어자가 private인 필드 접근해서 컨트롤러가 응답으로 를 주는 방법
1. 응답으로 제공하려는 필드의 제어자를 public으로 변경
2. 응답으로 제공하려는 필드에 대한 getter 메서드를 만들어 줌
1의 방법은 캡슐화가 깨진다. 2의 방법 역시 캡슐화가 깨지지만 public으로 만들면 setter와 getter를 동시에 노출한 것과 마찬가지이므로 캡슐화가 더 깨진다고 볼 수 있다.
레이어드 아키텍처
네 가지 계층
표현(Presentation) 계층
- Interface 계층 - Controller
- 클라이언트로부터 들어오는 요청을 받고, 응답해주는 역할
- 들어온 데이터를 값이 존재하는지, 타입이 맞는지 등 간단한 유효성 검사를 한 후 응용 계층으로 처리를 넘김
응용(Application) 계층
- Service
- 표현 계층에서 넘겨 받은 데이터로 새로운 자원(도메인 객체, Domain Object)을 저장하거나 저장되어 있던 자원을 조회, 조회된 자원의 메서드를 실행
도메인(Domain) 계층
- 도메인의 핵심적인 지식(비즈니스와 관련된 요구사항)이 있는 계층 - 도메인 객체
- 다른 계층이나 외부 요소에 의존하지 않도록 만드는것이 좋음
인프라스트럭처(Infrastructure) 계층
- 특정 인프라스트럭처에 접근하는 구현 코드들이 위치
- 리스트나 DB에 자원을 저장하는 로직이나 코드 등 포함
상품관리 레이어드 아키텍처의 계층별 의존성 방향
계층별로 패키지화
- application - SimpleProductService.java
- infrastructure - ListProductRepository.java
- domain - Product.java
- presentation - ProductController.java
도메인 객체
애플리케이션에서 사용되는 데이터와 그 데이터를 다루는 로직을 하나로 묶은 것으로 애플리케이션 핵심 지식(비즈니스 로직, 도메인 규칙, 정책)이 포함되어야 한다. 도메인 객체에 비즈니스 로직이 모이지 않고 비즈니스 로직이 서비스 코드에 구현되면 도메인 객체의 응집력이 낮아지고 서비스 코드와의 결합도가 증가하여 중복된 코드가 발생하고 유지보수하기 어려운 코드가 된다.
의존성
A라는 코드가 B 코드를 사용하기 때문에 A는 B에 의존적
빈 등록과 의존성 주입
스프링 프레임워크의 Application Context에서 @Configuration이 붙은 클래스들을 설정 정보로 저장해두고 @Bean이 붙은 메소드의 이름으로 빈 목록을 생성한다. 클라이언트가 해당 빈을 요청하면 ApplicationContext는 자신의 빈 목록에서 요청한 이름이 있는지 찾고, 설정 클래스로부터 빈 생성을 요청해서 생성된 빈을 응답한다.
의존성 주입 과정 - 생성자 주입
- 주입될 의존성(클래스)를 빈(Bean)으로 등록 : Service, Repository, RestController 같은 애너테이션이 달려 있는 클래스를 생성하여 빈으로 등록
- 빈으로 등록된 의존성을 사용할 곳에 주입 : @Autowired가 달려 있는 곳에 이미 생성되어 있는 빈을 가져와 주입
ProductController.java
private SimpleProductService simpleProductService;
@Autowired
ProductController(SimpleProductService simpleProductService){
this.simpleProductService = simpleProductService;
}
SimpleProductService.java
private ListProductRepository listProductRepository;
@Autowired
SimpleProductService(ListProductRepository listProductRepository){
this.listProductRepository = listProductRepository;
}
List에 상품 추가
1. 상품 List에 추가 : ListProductRepository.java
@Repository
public class ListProductRepository {
private List<Product> products = new CopyOnWriteArrayList<>();
public Product add(Product product) {
products.add(product);
return product;
}
}
CopyOnWriteArrayList 사용 이유
여러 개의 스레드가 동시에 동작하는 멀티 스레드 사용 환경에서는 Thread Safety한 컬렉션을 사용 해야 한다.
ArrayList는 안전성이 없는 컬렉션으로 주로 지역변수나 매개변수로 전달되는 리스트에 사용되며, 보통 하나의 스레드에만 접근하는 곳은 스레드 안정성이 필요하지 않기 때문에 사용한다.
2. 상품 저장하는 메서드 실행 : SimpleProductService.java
@Service
public class SimpleProductService {
private ListProductRepository listProductRepository;
@Autowired
SimpleProductService(ListProductRepository listProductRepository){
this.listProductRepository = listProductRepository;
}
public Product add(Product product){
Product saveProduct = listProductRepository.add(product);
return saveProduct;
}
}
3. 상품 데이터 응용계층으로 넘겨주기 : ProductController.java
@RestController
public class ProductController {
private SimpleProductService simpleProductService;
@Autowired
ProductController(SimpleProductService simpleProductService){
this.simpleProductService = simpleProductService;
}
@RequestMapping(value = "/products", method= RequestMethod.POST)
public Product createProduct(@RequestBody Product product){
return simpleProductService.add(product);
}
}
4. 코드 동작 확인 : Postman
5. id 추가하기
Product.java
id Setter 추가 : command+N -> Setter -> id 선택 후 OK
Setter 추가 해준 이유
id에 대한 책임을 Product가 아닌 ListProductRepository로 가져오면서 외부에서 id를 설정해야 할 필요가 있기 때문에 setter를 해줘야 한다. 캡슐화가 깨진 코드를 만들지 않기 위해 가급적 피해야 하기 때문에 필요시에만 작성한다.
id 자동으로 1씩 증가 : ListProductRepository.java
@Repository
public class ListProductRepository {
private List<Product> products = new CopyOnWriteArrayList<>();
private AtomicLong sequence = new AtomicLong(1L);
public Product add(Product product) {
product.setId(sequence.getAndAdd(1L));
products.add(product);
return product;
}
}
id Repository에서 관리하는 방법 선택 이유
Product 내에 static 변수로 관리시 다음장에서 Repository를 List관리에서 DB관리로 변경 할 때 Product를 찾아서 다시 변경해야 하기 때문이다.
6. 코드 동작 확인 : Postman
'프로젝트 > clone coding' 카테고리의 다른 글
[백엔드 개발 : ProductManagement] 상품 조회/수정/삭제 구현 (0) | 2025.03.19 |
---|---|
[백엔드 개발 : ProductManagement] DTO와 getter, setter (0) | 2025.03.18 |
[백엔드 개발 : ProductManagement] 상품관리 애플리케이션 정의 (0) | 2025.03.17 |
[Jump to SpringBoot : SBB] 프로젝트 개발과 서버 환경 분리 (0) | 2022.05.12 |
[Jump to SpringBoot : SBB] 프로젝트 AWS 서버에 배포하기 (0) | 2022.05.09 |