프로젝트 진행 상황을 쉽게 관리 가능
To Do, In Progress, Done 단계 설정
브랜치, 이슈와 연계하여 협업 가능
추가로 작업하면서 느낀 거는 JIRA를 사용하는 것에 편하고
유지보수성: 인터페이스를 통해 코드 변경 시 영향 최소화
유연성: DI(의존성 주입)를 통해 쉽게 교체 가능
테스트 용이성: Mock 객체를 활용한 단위 테스트 가능
@ExtendWith(MockitoExtension.class)
class OrderServiceTest {
@Mock
private ProductService productService;
@InjectMocks
private OrderService orderService;
@Test
void testCreateOrder() {
Product mockProduct = new Product("P001", "상품1", 100);
when(productService.getProduct("P001")).thenReturn(mockProduct);
Order order = orderService.createOrder(new OrderDto("P001", 1, "CARD"));
assertNotNull(order);
}
}
-Test-Double 전략
- Fake -> 핵사고날 관점이랑 비슷하다.
@SpringBootTest
@AutoConfigureMockMvc
class OrderControllerTest {
@Autowired
private MockMvc mockMvc;
@Test
void getProduct() throws Exception {
mockMvc.perform(get("/api/product/P001"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.id").value("P001"));
}
}
@SpringBootTest
@AutoConfigureMockMvc
class OrderAcceptanceTest {
@Test
void orderProcessTest() {
// 1. 상품 조회
// 2. 재고 확인
// 3. 결제 요청
// 4. 주문 생성
}
}
.gradle/
.idea/
build/
out/
package kr.co.youhost.sfep.fems.controller;
import kr.co.youhost.sfep.fems.response.ErrorResponse;
import kr.co.youhost.sfep.fems.response.SuccessCodeResponse;
import kr.co.youhost.sfep.fems.response.SuccessResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Slf4j
public class BaseRestController {
protected String getSignedAdminId() {
// XXX: 나중에 로그인 중인 id로 수정해야 함
return "admin";
}
protected ResponseEntity<?> getResponse(HttpStatus httpStatus, Object result, int size) {
if (size >= 0)
log.info("httpStatus={}, result={}", httpStatus, (result != null) ? result : "null");
else
log.info("httpStatus={}, result={}", httpStatus, size);
return new ResponseEntity<>(result, httpStatus);
}
protected ResponseEntity<?> getSuccessResponse(HttpStatus httpStatus, Object result, int size) {
SuccessResponse response = new SuccessResponse();
response.setData(result);
return getResponse(httpStatus, response, size);
}
protected ResponseEntity<?> getSuccessCodeResponse(HttpStatus httpStatus, String responseCode, Object result, int size) {
if (result == null) {
Map<String, String> simpleResponse = new HashMap<String, String>();
simpleResponse.put("responseCode", responseCode);
return getResponse(httpStatus, simpleResponse, size);
}
SuccessCodeResponse response = new SuccessCodeResponse();
response.setResponseCode(responseCode);
response.setData(result);
return getResponse(httpStatus, response, size);
}
protected ResponseEntity<?> getOkResponse(Object result) {
return getSuccessResponse(HttpStatus.OK, result, -1);
}
protected ResponseEntity<?> getOkResponse(String responseCode, Object result) {
return getSuccessCodeResponse(HttpStatus.OK, responseCode, result, -1);
}
protected ResponseEntity<?> getOkResponse(Map resultMap) {
return getSuccessResponse(HttpStatus.OK, resultMap, resultMap.size());
}
protected ResponseEntity<?> getOkListResponse(List resultList) {
int size = 0;
if (resultList != null)
size = resultList.size();
return getSuccessResponse(HttpStatus.OK, resultList, size);
}
protected ResponseEntity<?> getCreatedResponse(Object result) {
return getSuccessResponse(HttpStatus.CREATED, result, -1);
}
protected ResponseEntity<?> getErrorResponse(String errorMessage) {
log.error(errorMessage);
ErrorResponse response = new ErrorResponse();
response.setMessage(errorMessage);
return getResponse(HttpStatus.BAD_REQUEST, response, -1);
}
}
{
getProduct(); -> 상품이 없다면?
getStock(); -> 재고가 없다면?
payment(); -> 결제가 실패한다면?
decreaseStock(); -> 재고 감소 실패한다면?
createOrder();
}
@Transactional
public Order createOrder(OrderDto dto) {
Product product = productService.getProduct(dto.getProductId());
if (product == null) throw new ProductNotFoundException(dto.getProductId());
Stock stock = stockService.getStock(dto.getProductId());
if (stock.getStock() < dto.getQuantity()) throw new OutOfStockException();
Payment payment = paymentService.createPayment(new PaymentDto(dto.getOrderId(), dto.getAmount(), dto.getPaymentMethod()));
if (!payment.isSuccess()) throw new PaymentFailedException();
stockService.decreaseStock(dto.getProductId(), dto.getQuantity());
return orderRepository.save(new Order(dto));
}