Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

충남대 BE_김민성_1주차 과제(1단계~3단계) #165

Open
wants to merge 20 commits into
base: main
Choose a base branch
from

Conversation

castlekimdev
Copy link

step3.

본 애플리케이션에서 H2 DB만을 사용할게 분명한 상황이라고 생각합니다. 이 상황에서 Repository의 Interface를 두고 ~impl를 구현하는게 옳바른 방향인지 궁금합니다.

@nimunsang
Copy link

민성님 안녕하세요 ~ 😄
2단계를 함께하게 될 제로 멘토입니다.
6주간 잘 부탁드립니다 :)

Repository의 interface를 두고, ~impl을 구현하면 어떤 장점이 있을까요?
제 생각으로는, interface를 둠으로써

  1. 테스트하기 용이한 구조
  2. Repository를 사용하는 입장에서, 내부 구현을 몰라도 되는 구조
  3. 인터페이스는 그대로 두고, 내부 구현만을 갈아끼우기 용이한 구조
    가 될 것 같아요.

1주차 과제 잘 수행해주셨어요~
몇 가지 코멘트 달았는데, 한 번 생각해보시고 답변해보시면 좋을 것 같아요.
궁금한 점이 있다면 말씀해주세요 :)

@RestController
public class ProductController {

private final ProductRepository products;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ProductRepository와 같은 이름으로 변수명을 짓는 건 어떨까요?
private final ProductRepository productRepository;
Repository 성격임을 직관적으로 나타낼 수 있을 것 같아요~

Copy link
Author

@castlekimdev castlekimdev Jun 30, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

좋은것 같습니다! 감사합니다

this.products = products;
}

@GetMapping("/products")

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

각 mapping에 존재하는 "/products" 를 공통화할 수 있지 않을까요?
@RequestMapping 어노테이션을 사용해보세요~

Comment on lines 3 to 10
public record Product(
Long id,
String name,
Integer price,
String imageUrl
) {

}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Domain 객체인 Product를 class가 아닌 record로 사용하면 어떤 문제점이 발생할 수 있을지 고민해보세요!

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아래와 같은 문제점이 발생할 수 있을것 같아 class가 더 적합할것 같습니다!

  1. 이뮤터블한 특성으로 인한 제약

    • record는 불변이므로 상태 변경이 필요할 때 새로운 객체를 생성해야 합니다. 이는 성능과 메모리 사용 측면에서 비효율적일 수 있습니다. (예를 들어, Product 객체의 가격이나 재고 수량이 변할 수 있는 상황 등 )
  2. 프록시 객체 사용시 제약:

    • ORM 프레임워크나 AOP 등을 사용할 때 프록시 객체가 필요할 수 있습니다. record는 프록시 객체 생성에 적합하지 않을 수 있으며, 이러한 프레임워크와의 호환성이 떨어질 수 있습니다.
  3. 상속 지원 X

    • Product를 상속 구조로 표현하고 싶을 때 record로는 이를 구현하기 어렵습니다.

record는 간결하고 불변성을 보장하는 특성 때문에 도메인 객체보다 데이터 전송 객체(DTO) 등에서 더 유용할것 같다는 생각이 들었습니다!

import gift.controller.dto.ProductCreateRequestDto;
import gift.controller.dto.ProductResponseDto;
import gift.controller.dto.ProductUpdateRequestDto;
import gift.domain.Product;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Controller에서 Product domain에 대해 알 필요가 있을까요?
Service Layer에게 작업을 위임하는 것은 어떨까요?
Controller는 Request를 받고 --> Service가 Request를 받아 어떤 행위를 수행해서 Response를 넘겨주고 --> Controller가 그 Response 를 그저 넘겨주는 역할을 하면 어떨까요?

Copy link
Author

@castlekimdev castlekimdev Jun 30, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오 피드백 감사합니다!
다만 도메인 서비스는 도메인 클래스로 표현하기 어려운 도메인 규칙이나 비즈니스 로직을 캡슐화하는 역할을 한다고 생각하는데요!
그래서 지금 로직상으로는 단순 조회, 수정, 삭제으로 단순하여 도메인 서비스를 정의하지 않았습니다!

말씀해주신대로 Presentaion layer에서 domain을 직접 참조하는것에 대한 우려도 동시에 드는데 적절한 타협점에 대한 아이디어를 구하고 싶습니다 :)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

현재 구현 사항이 단순 CRUD 로직이라서, 굳이 클래스 하나 더 만들면서 서비스 레이어를 도입해야하는가? 에 대한 의문점이 분명히 있으실텐데요~ 저도 공감합니다.
다만, 현재 로직을 보면, controller의 역할이라고는 볼 수 없는, "요청을 받아 ~ 응답을 던진다" 라는 행위 외에도 update를 수행하고, dto를 객체로 mapping 하는 것과 같은 행위(비즈니스 로직)도 수행하고 있어요. 이러한 과정은 Controller (Presentation Layer)의 관심사는 아니라고 생각해요.
https://incheol-jung.gitbook.io/docs/q-and-a/architecture/ddd
한 번 읽어보셔도 좋을 것 같아요!

) {
Product originalProduct = products.get(id);
if (originalProduct == null) {
throw new RuntimeException("Product not found");

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

조금 더 유의미한 메시지를 던지기 위해, Product not found, productId : {} 처럼 넘겨주면 좋을 것 같아요.
RuntimeException 대신, 조금 더 구체적인 Exception을 던지는 것은 어떨까요? NoSuchElementException도 가능할 것 같아요~

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

좋습니다 exception message는 NoSuchElementException 클래스 안에 캡슐화 해보겠습니다 :)


private final JdbcTemplate jdbcTemplate;

private final RowMapper<Product> rowMapper = (rs, rowNum) -> new Product(

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

private static final 이 되어도 좋을 것 같아요~
어떤 차이점이 있을까요?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

메모리를 효율적으로 사용할 수 있고 오버헤드 비용 감소하는 이점이 있을것 같습니다!
구체적으로 static 필드는 클래스 로드 시 한 번만 초기화되고. 모든 인스턴스가 하나의 rowMapper를 사용하게 되어 메모리를 효율적으로 사용할 수 있을것이고 클래스가 로드될 때 한 번만 초기화되므로, 여러 인스턴스를 생성할 때 초기화 비용이 줄어들것 같습니다 :)

Comment on lines 1 to 6
create table `products` (
`id` bigint auto_increment primary key,
`name` varchar(255) not null,
`price` int not null,
`image_url` varchar(255) not null
);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

테이블 설계 잘 해주셨는데,
application.properties (application.yml) 에 database 관련 설정을 안해주신 것 같아요~

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

앗 누락됐네요 추가해보겠습니다!

@castlekimdev castlekimdev marked this pull request as draft June 30, 2024 14:18
@castlekimdev
Copy link
Author

@nimunsang 피드백 감사합니다!
피드백 반영 및 궁금한 점들을 몇가지 남겨보았습니다 :)

@castlekimdev castlekimdev marked this pull request as ready for review June 30, 2024 14:34
@castlekimdev castlekimdev requested a review from nimunsang June 30, 2024 14:35
Copy link

@nimunsang nimunsang left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

민성님~ 요구사항 잘 반영해주셨네요 👍🏻
몇 가지 더 코멘트 남겼는데, 한 번 확인해보세요!

Comment on lines 23 to 25
public void setId(Long id) {
this.id = id;
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

모든 setter 가 현재 코드에서 사용되지 않는 것 같은데, 사용하지 않는 setter는 닫아두는 것이 어떨까요~?

Comment on lines 40 to 41
.orElseThrow(() -> new RuntimeException("Product not found"));
return ProductResponseDto.from(product);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NoSuchElementException을 활용해보셔도 좋을 것 같아요~

Comment on lines 19 to 21
public Long id() {
return id;
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

record에서 class로 변환된 만큼, getId() 처럼 get을 prefix로 붙이는 관행을 따르면 좋을 것 같아요~

Comment on lines 1 to 15
package gift.controller;

public class NoSuchElementException extends RuntimeException {

private final Long productId;

public NoSuchElementException(Long productId) {
super("Product not found with id: " + productId);
this.productId = productId;
}

public Long getProductId() {
return productId;
}
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NoSuchElementException을 따로 구현해주시는 것도 좋지만,
이미 구현되어 있는 것을 사용하면 어떨까요?
java.util 패키지에 이미 구현되어 있어요~!

@@ -1 +1,6 @@
spring.application.name=spring-gift
spring.h2.console.enabled=true
spring.datasource.url=jdbc:h2:mem:test
spring.sql.init.schema-locations=classpath:/static/sql/schema.sql

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

schema.sql, data.sql이 정확한 위치에 없어서 에러가 발생할 것 같아요~

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

와 매의눈 이십니다!! 👍

@@ -15,7 +15,7 @@
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RestController("/products")

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@RequestMapping 과 헷갈리신 것 같아요 😅

@castlekimdev
Copy link
Author

@nimunsang 피드백 반영해보았습니다! 덕분에 많이 배웠습니다 감사합니다 🙇

@castlekimdev castlekimdev requested a review from nimunsang July 1, 2024 03:20
@nimunsang
Copy link

@nimunsang 피드백 반영해보았습니다! 덕분에 많이 배웠습니다 감사합니다 🙇

고생하셨어요 👍🏻 👍🏻 Base 브랜치만 바꿔주시면 머지하겠습니다~

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants