[내일배움캠프 본캠프] 1일 차 TIL 상품-주문 미니프로젝트 피드백

2026. 2. 10. 16:47·내배캠

내일배움캠프 본캠프

본캠프가 어제부로 시작했다.
어제는 참여하지 못해서 오늘부터 다시 열심히 til을 작성하려고 한다.
오늘은 스프링의 입문강의를 수강하고
이전에 진행한 미니프로젝트에 대한 피드백을 받았다.

Spring 입문주차 1강

1강에서 기본적인 CRUD와 RESTFul 설계 학습을 위한 메모장 프로젝트 강의가 있었다.
강의에서 나온 프로젝트 설계에 맞춰 먼저 구현해 보고 강의내용과 비교해보려고 한다.


근데 그전에 강의를 대충 훑어보니 DB를 사용하지 않고 진행하는 걸로 보아 HashMap 이용해 저장하는 것으로 보인다.
그 후 DB강의 후 DB 연동과 더불어 SQL과 JDBC 설명을 하고
DB를 사용한 CRUD 설명으로 이어지는 것 같다.

새로 배우게 될 내용은 HashMap과 stream메서드 사용법, 요청 Dto, 응답 Dto 정도가 될 것 같다.

메모장 프로젝트

프로젝트 생성
Name: memo
Language: Java
Build system: Gradle - Groovy
Group: com.sparta
JDK: 17
Dependences : Spring Web, Lombok, Thymeleaf

 

메모장 기능 설계
1. 접속하자마자 메모 전체 목록 조회하기
- GET API 사용해서 메모 목록 불러오기


2. 메모 생성하기
-POST API 사용해서 메모 신규 생성하기
-생성된 메모 반환

 

3. 메모 변경하기
- PUT API 사용해서 메모 내용 변경하기
- 사용자가 클릭한 메모가 DB에 존재하는지 확인하기
- 해당 메모 내용 변경


4. 메모 삭제하기
- DELETE API 사용해서 메모 삭제하기
- 사용자가 삭제하려는 메모가 DB에 존재하는지 확인하기
- DB에서 해당 메모 삭제

메모장 API

기능 Method URL Return
메모 생성하기 POST /api/memos MemoReposnseDto
메모 조회하기 GET /api/memos List<MemoReponseDto>
메모 변경하기 PUT /api/memos/{id} Long
메모 삭제하기 DELETE /api/memos/{id} Long

상품-주문 미니프로젝트 피드백

이전에 진행한 상품-주문 도메인으로 구성된 프로젝트 과제에 대한 피드백을 해 주셨다.

1. DDD
2. 코드 깔끔하게 쓰는 방법 - static factory 패턴, private 메서드
3. 영속성 컨텍스트와 @Transactional의 관계
4. 원자성 - 낙관락, 비관락, 분산락
5. @Builder의 access 범위 설정

1. DDD란?

도메인 중심 설계로, 따로 라이브러리가 있거나 한 건 아니고
엔티티에 더 많은 역할을 부여하고
서비스에서는 비즈니스로직만을 다루게 하여 코드를 줄인 것이다.

예를 들어 상품 엔티티에
상품 생성
상품의 재고 관리 등을 담당한다.

또한 상품에 여러 개의 필드들이 있을 때
연관 관계로 매핑하는 것보다
@Embedded + @Embeddable 방식을 쓰면
도메인의 의미도 한 곳에 모으고
DB에 저장 시에도 임베딩한 객체를 펼 처서 저장하게 되어
별도의 조인작업이 필요하지 않다.

또한 예를 들어 상품의 가격이나 재고를 임베딩하여 값을 저장하면
가격파트는 가격 객체로,
재고는 재고 객체로 관리할 수 있어
책임이 명확해진다.

2. 코드 깔끔하게 쓰는 방법 - static factory 패턴, private 메서드

- 파라미터가 3개 이상부터 생성자를 따로 private 메서드로 만들어서 관리하면
코드가 좀 더 깔끔해진다.
아래는 엔티티를 생성하는 메서드를 static 적용한 모습이다.

public class Product {
    // 필드 설정 ...
    private Product(String name, Integer price, Integer stock) {
        this.name = name;
        this.price = price;
        this.stock = stock;
    }

    public static Product create(String name, Integer price, Integer stock) {
        return new Product(name, price, stock);
    }
}

 

 

추가로 엔티티 -> DTO 변환 시에도 아래와 같이 사용된다.

public class ProductResponse {
    private Long id;
    private String name;
    private Integer price;
    private Integer stock;

    public static ProductResponse from(Product product) {
        return new ProductResponse(product.getId(),
                product.getName(),
                product.getPrice(),
                product.getStock()
        );
    }
}

 

- 서비스 레이어에서 반복되는 조회 메서드는 private 필드로 만들어 관리하자.

public class ProductService {
    @Transactional(readOnly = true)
    public ProductResponse findOne(Long id) {
        Product product = findProduct(id); // 첫 번째 private 메서드 호출
        return ProductResponse.from(product); // 첫 번째 엔티티 -> DTO 변환 메서드
    }

    public ProductResponse update(Long id, ProductRequest dto) {
        Product product = findProduct(id); // 두 번째 private 메서드 호출
        product.update(dto.getName(), dto.getPrice(), dto.getStock());
        return ProductResponse.from(product); // 두 번째 엔티티 -> DTO 변환 메서드
    }

    public void delete(Long id) {
        Product product = findProduct(id); // 세 번째 private 메서드 호출
        productRepository.delete(product);
    }

    private Product findProduct(Long id){
        return productRepository.findById(id)
                .orElseThrow(()-> new RuntimeException("해당 상품이 없습니다."));
    }
}

생성/전체조회 작업만 제외하면
전부 단일 조회 메서드가 사용되기에 코드를 많이 줄일 수 있다.


그리고 엔티티 -> DTO 변환도 메서드로 따로 관리해 코드를 줄였다.

3. 영속성 컨텍스트와 @Transactional의 관계

서비스 클래스에 @Transactional을 붙여야 하는 이유?
기본적으로 JPA는 영속성 컨텍스트를 생성하고 조회하는 식으로
Drity Checking이 이루어진다.

이때 조회메서드도 Lazy Loading이 이루어지기에
불필요한 작업이 포함될 수 있어
readOnly = true 속성을 추가해 성능을 높일 수 있다.

수정메서드에서 save() 메서드를 사용하지 않아도 저장이 되는 이유는
@Transactional 이 영속성 컨텍스트를 기반으로 동작하기 때문이다.
만약 이 어노테이션을 붙이지 않으면 저장이 되지 않는다.

4. 원자성 - 낙관락, 비관락, 분산락

재고를 변경하거나 좌석을 구매하는 등
사용자들 간의 데이터 변경이 민감하게 작용하는 분야에서는
원자성을 유지하는 것이 중요하다.

여기서 원자성은 한 명이 해당 자원(좌석, 재고 등)을 수정 중일 때
다른 사람이 수정하지 못하게 제한하여 지켜줄 수 있다.

이때 Lock 방법이 여러 개인데
대표적으로
낙관락, 비관락, 분산락이 있다.

4-1 낙관락
일단 실행하고 문제가 생기면 롤백하는 방식이다.
객체가 변경됐는지 확인하는 용도의 필드를 하나 만들어서
@Version 어노테이션을 붙여
쿼리 실행 시 해당 칼럼을 비교해 같으면 실행
값이 다르면 에러를 내는 방식이다.

public class Product {
    // 다른 필드들 ...
    @Version
    private Long version;
}

기본적으로 한번 변경할 때마다 해당 필드의 값을 +1 하여 저장한다.

// 주문 생성
public Long save(OrderRequest orderRequest) {
    Product product = productRepository.findByNameAndPrice(
                orderRequest.getProductName(),
                orderRequest.getProductPrice()
            ).orElseThrow(()->new RuntimeException("[저장시 에러]-해당 상품 없음"));

    // 새로 주문한 주문 수량
    Integer diff = orderRequest.getQuantity();
    Order order = new Order(product,diff);
    orderRepository.save(order);
    // 이 자리에 재고 차감 메서드 실행, 주문 생성은 무조건 그 만큼 재고 차감
    product.changeStock(-diff);
    return order.getId();
}

새로운 주문이 생성될 때
주문한 상품의 수량만큼 해당 상품의 재고를 차감하는 방식이다.

product.changeStock(-diff); 에서 version을 +1 하고 재고를 변경한다.

public class Product {
    // ... 다른 필드, 메서드들
    
    // 재고 증가/차감 메서드
    public void changeStock(Integer diff){
        if(diff < 0 && diff + this.stock <0){
            throw new RuntimeException("재고가 부족합니다.");
        }
        this.stock += diff;
    }
}



4-2 비관락
해당 자원에 접근 시 잠그고 시작하고
커밋이 해제하는 방식이다.

쿼리문에 for update를 추가해 사용할 수 있다.

4-3 분산락
락을 DB단위가 아니라 서버단위로 실행하는 방식이다.
그래서 한 서버가 사용할 때 락을 걸어 다른 서버에서 사용하지 못하게 하는 방식이다.

5. @Builder의 access 범위 설정

static factory 패턴 적용 시
엔티티에 @Builder를 적용하면
서비스 레이어 코드의 가독성을 높일 수 있다.

근데 이때 빌더는 필드에 값을 setter 방식으로 설정 후 메서드 체이닝 방식으로 반환하기에
불변성을 꺠뜨릴 수 있는데 이를 접근 범위를 설정하여 해결할 수 있다.

@Builder(access = AccessLevel.PRIVATE)
@Entity
public class Product{

}

 

작업한 깃허브 리포지토리 Url - https://github.com/RunSBS/sparta-mini1

'내배캠' 카테고리의 다른 글

[내일배움캠프 본캠프] 3일 차 TIL 영속성 컨텍스트  (0) 2026.02.12
[내일배움캠프 본캠프] 2일 차 TIL - SQL, JdbcTemplate, IoC, DI, Bean  (0) 2026.02.11
[내일배움캠프 사전캠프] 3일 차 TIL Jackson  (0) 2026.02.06
[내일배움캠프 사전캠프] 2일 차 TIL 상품-주문 미니프로젝트  (0) 2026.02.05
[내일배움캠프 사전캠프] 1일 차 TIL 상품-주문 미니프로젝트  (0) 2026.02.04
'내배캠' 카테고리의 다른 글
  • [내일배움캠프 본캠프] 3일 차 TIL 영속성 컨텍스트
  • [내일배움캠프 본캠프] 2일 차 TIL - SQL, JdbcTemplate, IoC, DI, Bean
  • [내일배움캠프 사전캠프] 3일 차 TIL Jackson
  • [내일배움캠프 사전캠프] 2일 차 TIL 상품-주문 미니프로젝트
MvA
MvA
백엔드 개발자 김재현입니다. 주로 공부하면서 느낀점을 기록합니다.
  • MvA
    Man vs Ai
    MvA
  • 전체
    오늘
    어제
    • 분류 전체보기 (94)
      • Java (6)
      • Python (8)
        • 딥러닝 (1)
        • 머신러닝 (7)
      • JavaScript (2)
      • 내배캠 (60)
      • 개인 프로젝트 (11)
      • 책 후기 (5)
      • 기타 (1)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    배포
    딥러닝
    아키텍처
    TiL
    내일배움캠프
    머신러닝
    Riot API
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.4
MvA
[내일배움캠프 본캠프] 1일 차 TIL 상품-주문 미니프로젝트 피드백
상단으로

티스토리툴바